add support for split demo files (UNLOZE)

This commit is contained in:
BotoX 2022-09-12 22:20:16 +02:00
parent 3b723a64e0
commit d4a0d51bfc
9 changed files with 121 additions and 51 deletions

View File

@ -8,32 +8,49 @@
int main(const int argc, const char* argv[]) int main(const int argc, const char* argv[])
{ {
if (argc != 2) if (argc < 2)
{ {
fprintf(stderr, "Usage: %s <in>.dem\n", argv[0]); fprintf(stderr, "Usage: %s <in>.dem [in2.dem] ...\n", argv[0]);
return -1; return -1;
} }
std::filesystem::path inputFile(argv[1]); DemoReader::Init();
FILE* inputFp = fopen(inputFile.c_str(), "rb"); SourceGameContext *context = nullptr;
if (!inputFp)
bool error = false;
for (int i = 1; i < argc; i++)
{ {
fprintf(stderr, "Error: Could not open input file\n"); std::filesystem::path inputFile(argv[i]);
return -1; FILE* inputFp = fopen(inputFile.c_str(), "rb");
if (!inputFp)
{
fprintf(stderr, "Error: Could not open input file\n");
return -1;
}
static std::filesystem::path outputDir = inputFile.filename().replace_extension();
static std::filesystem::path outputDirVoice = outputDir.string() + "/voice";
if (i == 1)
{
std::filesystem::create_directory(outputDir);
std::filesystem::create_directory(outputDirVoice);
context = new SourceGameContext(outputDir, outputDirVoice);
if(!context->init())
return -1;
}
bool dirty = DemoReader::ProcessDem(inputFp, context);
if(dirty)
error = true;
fclose(inputFp);
} }
std::filesystem::path outputDir = inputFile.filename().replace_extension(); context->End();
std::filesystem::path outputDirVoice = outputDir.string() + "/voice"; delete context;
std::filesystem::create_directory(outputDir);
std::filesystem::create_directory(outputDirVoice);
SourceGameContext context = SourceGameContext(outputDir, outputDirVoice); DemoReader::DeInit();
if (!context.init())
return -1;
bool error = !DemoReader::ProcessDem(inputFp, &context);
fclose(inputFp);
return error; return error;
} }

View File

@ -25,21 +25,31 @@ Logic::~Logic()
void Logic::Start() void Logic::Start()
{ {
data["demoheader"] = json({ tickBase = curTick;
{"demofilestamp", context->header.demofilestamp}, if(curTick)
{"demoprotocol", context->header.demoprotocol}, {
{"networkprotocol", context->header.networkprotocol}, data["demoheader"]["playback_time"] = data["demoheader"]["playback_time"].get<float>() + context->header.playback_time;
{"servername", context->header.servername}, data["demoheader"]["playback_ticks"] = data["demoheader"]["playback_ticks"].get<int32_t>() + context->header.playback_ticks;
{"clientname", context->header.clientname}, data["demoheader"]["playback_frames"] = data["demoheader"]["playback_frames"].get<int32_t>() + context->header.playback_frames;
{"mapname", context->header.mapname}, }
{"gamedirectory", context->header.gamedirectory}, else
{"playback_time", context->header.playback_time}, {
{"playback_ticks", context->header.playback_ticks}, data["demoheader"] = json({
{"playback_frames", context->header.playback_frames}, {"demofilestamp", context->header.demofilestamp},
{"signonlength", context->header.signonlength} {"demoprotocol", context->header.demoprotocol},
}); {"networkprotocol", context->header.networkprotocol},
{"servername", context->header.servername},
{"clientname", context->header.clientname},
{"mapname", context->header.mapname},
{"gamedirectory", context->header.gamedirectory},
{"playback_time", context->header.playback_time},
{"playback_ticks", context->header.playback_ticks},
{"playback_frames", context->header.playback_frames},
{"signonlength", context->header.signonlength}
});
}
// std::cout << data.dump(2, ' ', false, json::error_handler_t::replace) << "\n"; // std::cout << data["demoheader"].dump(2, ' ', false, json::error_handler_t::replace) << "\n";
} }
void Logic::Finish(bool dirty) void Logic::Finish(bool dirty)
@ -59,9 +69,12 @@ void Logic::Finish(bool dirty)
{ {
data["demoheader"]["playback_ticks"] = curTick; data["demoheader"]["playback_ticks"] = curTick;
data["demoheader"]["playback_time"] = curTick * context->fTickInterval; data["demoheader"]["playback_time"] = curTick * context->fTickInterval;
data["demoheader"]["playback_frames"] = context->curFrame; data["demoheader"]["playback_frames"] = data["demoheader"]["playback_frames"].get<int32_t>() - context->header.playback_frames + context->curFrame;
} }
}
void Logic::End()
{
data["voice"]["total_time"] = voiceTotalTime; data["voice"]["total_time"] = voiceTotalTime;
data["voice"]["active_time"] = voiceActiveTime; data["voice"]["active_time"] = voiceActiveTime;
@ -339,4 +352,4 @@ void Logic::OnRoundEnd(const char *message, int reason, int winner)
}; };
data["events"] += round_end; data["events"] += round_end;
} }

View File

@ -18,6 +18,7 @@ struct Logic
void Start(); void Start();
void Finish(bool dirty); void Finish(bool dirty);
void End();
struct struct
{ {
@ -41,6 +42,7 @@ struct Logic
void OnRoundStart(int timelimit); void OnRoundStart(int timelimit);
void OnRoundEnd(const char *message, int reason, int winner); void OnRoundEnd(const char *message, int reason, int winner);
int32_t tickBase = 0;
int32_t curTick = 0; int32_t curTick = 0;
float voiceTotalTime = 0.0f; float voiceTotalTime = 0.0f;
float voiceActiveTime = 0.0f; float voiceActiveTime = 0.0f;

View File

@ -52,6 +52,9 @@ bool SourceGameContext::init()
} }
voiceWriter = new VoiceDataWriter(this, outputDirVoice.c_str()); voiceWriter = new VoiceDataWriter(this, outputDirVoice.c_str());
if(!voiceWriter->init())
return false;
logic = new Logic(this); logic = new Logic(this);
return true; return true;
} }
@ -64,8 +67,20 @@ void SourceGameContext::Start()
void SourceGameContext::Finish(bool dirty) void SourceGameContext::Finish(bool dirty)
{ {
voiceWriter->Finish();
logic->Finish(dirty); logic->Finish(dirty);
voiceWriter->Finish();
for(int i = 0; i < MAX_PLAYERS; i++)
players[i].connected = false;
curTick = -1;
curFrame = -1;
}
void SourceGameContext::End()
{
logic->End();
voiceWriter->End();
} }
void SourceGameContext::StartCommandPacket(const CommandPacket& packet) void SourceGameContext::StartCommandPacket(const CommandPacket& packet)
@ -76,7 +91,7 @@ void SourceGameContext::StartCommandPacket(const CommandPacket& packet)
return; return;
curTick = packet.tick; curTick = packet.tick;
logic->curTick = curTick; logic->curTick = logic->tickBase + curTick;
voiceWriter->StartCommandPacket(packet); voiceWriter->StartCommandPacket(packet);
} }

View File

@ -75,6 +75,7 @@ struct SourceGameContext
void Start(); void Start();
void Finish(bool dirty); void Finish(bool dirty);
void End();
void StartCommandPacket(const CommandPacket& packet); void StartCommandPacket(const CommandPacket& packet);
void EndCommandPacket(const PacketTrailingBits& trailingBits); void EndCommandPacket(const PacketTrailingBits& trailingBits);

View File

@ -10,6 +10,9 @@
#include "sourcesdk/bitbuf.h" #include "sourcesdk/bitbuf.h"
#include <cstdint> #include <cstdint>
static NetHandlers::NetDataStructArray s_netDataStructs;
static DemHandlers::DemDataStructArray s_demDataStructs;
PacketTrailingBits ParsePacket(uint8_t* packet, size_t length, PacketTrailingBits ParsePacket(uint8_t* packet, size_t length,
SourceGameContext& context, SourceGameContext& context,
const NetHandlers::NetDataStructArray& netDataStructs) const NetHandlers::NetDataStructArray& netDataStructs)
@ -38,13 +41,20 @@ PacketTrailingBits ParsePacket(uint8_t* packet, size_t length,
return trailingBits; return trailingBits;
} }
void DemoReader::Init()
{
NetHandlers::CreateNetMsgStructs(s_netDataStructs);
DemHandlers::CreateDemMsgStructs(s_demDataStructs);
}
void DemoReader::DeInit()
{
DemHandlers::DestroyDemMsgStructs(s_demDataStructs);
NetHandlers::DestroyNetMsgStructs(s_netDataStructs);
}
bool DemoReader::ProcessDem(std::FILE* inputFp, SourceGameContext* context) bool DemoReader::ProcessDem(std::FILE* inputFp, SourceGameContext* context)
{ {
NetHandlers::NetDataStructArray netDataStructs;
DemHandlers::DemDataStructArray demDataStructs;
NetHandlers::CreateNetMsgStructs(netDataStructs);
DemHandlers::CreateDemMsgStructs(demDataStructs);
DemoFileReader reader(inputFp); DemoFileReader reader(inputFp);
{ {
reader.ReadDemoHeader(context->header); reader.ReadDemoHeader(context->header);
@ -59,7 +69,7 @@ bool DemoReader::ProcessDem(std::FILE* inputFp, SourceGameContext* context)
do do
{ {
reader.ReadCmdHeader(packet.cmd, packet.tick); reader.ReadCmdHeader(packet.cmd, packet.tick);
packet.data = demDataStructs[packet.cmd]; packet.data = s_demDataStructs[packet.cmd];
DemHandlers::DemMsg_FileRead(packet.cmd, reader, packet.data); DemHandlers::DemMsg_FileRead(packet.cmd, reader, packet.data);
PacketTrailingBits trailingBits = PacketTrailingBits(); PacketTrailingBits trailingBits = PacketTrailingBits();
@ -68,7 +78,7 @@ bool DemoReader::ProcessDem(std::FILE* inputFp, SourceGameContext* context)
if (packet.cmd == dem_packet || packet.cmd == dem_signon) if (packet.cmd == dem_packet || packet.cmd == dem_signon)
{ {
Array<uint8_t> buffer = reader.ReadRawData(NET_MAX_PAYLOAD); Array<uint8_t> buffer = reader.ReadRawData(NET_MAX_PAYLOAD);
trailingBits = ParsePacket(buffer.begin(), buffer.length(), *context, netDataStructs); trailingBits = ParsePacket(buffer.begin(), buffer.length(), *context, s_netDataStructs);
} }
else if (packet.cmd == dem_stringtables) else if (packet.cmd == dem_stringtables)
@ -100,8 +110,5 @@ bool DemoReader::ProcessDem(std::FILE* inputFp, SourceGameContext* context)
context->Finish(dirty); context->Finish(dirty);
DemHandlers::DestroyDemMsgStructs(demDataStructs); return dirty;
NetHandlers::DestroyNetMsgStructs(netDataStructs);
return !dirty;
} }

View File

@ -5,5 +5,7 @@
namespace DemoReader namespace DemoReader
{ {
void Init();
void DeInit();
bool ProcessDem(std::FILE* inputFp, SourceGameContext* context); bool ProcessDem(std::FILE* inputFp, SourceGameContext* context);
} }

View File

@ -138,16 +138,26 @@ VoiceDataWriter::VoiceDataWriter(SourceGameContext* context, const char* outputP
{ {
} }
void VoiceDataWriter::Start() bool VoiceDataWriter::init()
{ {
int error = CELT_OK; int error = CELT_OK;
const CeltConfig& config = sCeltConfigs[sQuality]; const CeltConfig& config = sCeltConfigs[sQuality];
m_celtMode = celt_mode_create(config.sampleRate, config.frameSizeSamples, &error); m_celtMode = celt_mode_create(config.sampleRate, config.frameSizeSamples, &error);
assert(error == CELT_OK); assert(error == CELT_OK);
assert(m_celtMode); assert(m_celtMode);
return error == CELT_OK;
}
void VoiceDataWriter::Start()
{
m_tickBase = m_curTick;
} }
void VoiceDataWriter::Finish() void VoiceDataWriter::Finish()
{
}
void VoiceDataWriter::End()
{ {
if(m_isSilenced) if(m_isSilenced)
{ {
@ -175,7 +185,7 @@ void VoiceDataWriter::Finish()
void VoiceDataWriter::StartCommandPacket(const CommandPacket& packet) void VoiceDataWriter::StartCommandPacket(const CommandPacket& packet)
{ {
m_lastTick = m_curTick; m_lastTick = m_curTick;
m_curTick = packet.tick; m_curTick = m_tickBase + packet.tick;
} }
void VoiceDataWriter::EndCommandPacket(const PacketTrailingBits& trailingBits) void VoiceDataWriter::EndCommandPacket(const PacketTrailingBits& trailingBits)
@ -184,8 +194,8 @@ void VoiceDataWriter::EndCommandPacket(const PacketTrailingBits& trailingBits)
if (m_curTick <= tickMargin) if (m_curTick <= tickMargin)
return; return;
// Skip silence if noone talks for at least 3 seconds // Skip silence if noone talks for at least 1.5 seconds
if((m_curTick - m_lastVoiceTick) / context->fTickRate > 3.0) if((m_curTick - m_lastVoiceTick) / context->fTickRate > 1.5)
{ {
if(!m_isSilenced) if(!m_isSilenced)
{ {

View File

@ -54,9 +54,11 @@ class VoiceDataWriter
{ {
public: public:
VoiceDataWriter(SourceGameContext *context, const char* outputPath); VoiceDataWriter(SourceGameContext *context, const char* outputPath);
bool init();
void Start(); void Start();
void Finish(); void Finish();
void End();
void StartCommandPacket(const CommandPacket& packet); void StartCommandPacket(const CommandPacket& packet);
void EndCommandPacket(const PacketTrailingBits& trailingBits); void EndCommandPacket(const PacketTrailingBits& trailingBits);
@ -95,6 +97,7 @@ private:
eCodec m_Codec = CODEC_NONE; eCodec m_Codec = CODEC_NONE;
public: public:
int32_t m_tickBase = 0;
bool m_isSilenced = false; bool m_isSilenced = false;
std::vector<std::pair<int32_t, int32_t>> m_silence; std::vector<std::pair<int32_t, int32_t>> m_silence;
}; };