From d4a0d51bfcbb7a33ae1fccd69399c95848163db4 Mon Sep 17 00:00:00 2001 From: BotoX Date: Mon, 12 Sep 2022 22:20:16 +0200 Subject: [PATCH] add support for split demo files (UNLOZE) --- demboyz/demboyz.cpp | 53 ++++++++++++++-------- demboyz/game/logic.cpp | 45 +++++++++++------- demboyz/game/logic.h | 2 + demboyz/game/sourcecontext.cpp | 19 +++++++- demboyz/game/sourcecontext.h | 1 + demboyz/io/demoreader.cpp | 29 +++++++----- demboyz/io/demoreader.h | 2 + demboyz/io/voicewriter/voicedatawriter.cpp | 18 ++++++-- demboyz/io/voicewriter/voicedatawriter.h | 3 ++ 9 files changed, 121 insertions(+), 51 deletions(-) diff --git a/demboyz/demboyz.cpp b/demboyz/demboyz.cpp index ff31b11..71de343 100644 --- a/demboyz/demboyz.cpp +++ b/demboyz/demboyz.cpp @@ -8,32 +8,49 @@ int main(const int argc, const char* argv[]) { - if (argc != 2) + if (argc < 2) { - fprintf(stderr, "Usage: %s .dem\n", argv[0]); + fprintf(stderr, "Usage: %s .dem [in2.dem] ...\n", argv[0]); return -1; } - std::filesystem::path inputFile(argv[1]); - FILE* inputFp = fopen(inputFile.c_str(), "rb"); - if (!inputFp) + DemoReader::Init(); + SourceGameContext *context = nullptr; + + bool error = false; + for (int i = 1; i < argc; i++) { - fprintf(stderr, "Error: Could not open input file\n"); - return -1; + std::filesystem::path inputFile(argv[i]); + 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(); - std::filesystem::path outputDirVoice = outputDir.string() + "/voice"; - std::filesystem::create_directory(outputDir); - std::filesystem::create_directory(outputDirVoice); + context->End(); + delete context; - SourceGameContext context = SourceGameContext(outputDir, outputDirVoice); - if (!context.init()) - return -1; - - bool error = !DemoReader::ProcessDem(inputFp, &context); - - fclose(inputFp); + DemoReader::DeInit(); return error; } diff --git a/demboyz/game/logic.cpp b/demboyz/game/logic.cpp index 70ef614..c49e32f 100644 --- a/demboyz/game/logic.cpp +++ b/demboyz/game/logic.cpp @@ -25,21 +25,31 @@ Logic::~Logic() void Logic::Start() { - data["demoheader"] = json({ - {"demofilestamp", context->header.demofilestamp}, - {"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} - }); + tickBase = curTick; + if(curTick) + { + data["demoheader"]["playback_time"] = data["demoheader"]["playback_time"].get() + context->header.playback_time; + data["demoheader"]["playback_ticks"] = data["demoheader"]["playback_ticks"].get() + context->header.playback_ticks; + data["demoheader"]["playback_frames"] = data["demoheader"]["playback_frames"].get() + context->header.playback_frames; + } + else + { + data["demoheader"] = json({ + {"demofilestamp", context->header.demofilestamp}, + {"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) @@ -59,9 +69,12 @@ void Logic::Finish(bool dirty) { data["demoheader"]["playback_ticks"] = curTick; data["demoheader"]["playback_time"] = curTick * context->fTickInterval; - data["demoheader"]["playback_frames"] = context->curFrame; + data["demoheader"]["playback_frames"] = data["demoheader"]["playback_frames"].get() - context->header.playback_frames + context->curFrame; } +} +void Logic::End() +{ data["voice"]["total_time"] = voiceTotalTime; data["voice"]["active_time"] = voiceActiveTime; @@ -339,4 +352,4 @@ void Logic::OnRoundEnd(const char *message, int reason, int winner) }; data["events"] += round_end; -} \ No newline at end of file +} diff --git a/demboyz/game/logic.h b/demboyz/game/logic.h index 7d1c640..0271b6d 100644 --- a/demboyz/game/logic.h +++ b/demboyz/game/logic.h @@ -18,6 +18,7 @@ struct Logic void Start(); void Finish(bool dirty); + void End(); struct { @@ -41,6 +42,7 @@ struct Logic void OnRoundStart(int timelimit); void OnRoundEnd(const char *message, int reason, int winner); + int32_t tickBase = 0; int32_t curTick = 0; float voiceTotalTime = 0.0f; float voiceActiveTime = 0.0f; diff --git a/demboyz/game/sourcecontext.cpp b/demboyz/game/sourcecontext.cpp index b27fedc..2eb372b 100644 --- a/demboyz/game/sourcecontext.cpp +++ b/demboyz/game/sourcecontext.cpp @@ -52,6 +52,9 @@ bool SourceGameContext::init() } voiceWriter = new VoiceDataWriter(this, outputDirVoice.c_str()); + if(!voiceWriter->init()) + return false; + logic = new Logic(this); return true; } @@ -64,8 +67,20 @@ void SourceGameContext::Start() void SourceGameContext::Finish(bool dirty) { - voiceWriter->Finish(); 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) @@ -76,7 +91,7 @@ void SourceGameContext::StartCommandPacket(const CommandPacket& packet) return; curTick = packet.tick; - logic->curTick = curTick; + logic->curTick = logic->tickBase + curTick; voiceWriter->StartCommandPacket(packet); } diff --git a/demboyz/game/sourcecontext.h b/demboyz/game/sourcecontext.h index 43985f2..7db7a09 100644 --- a/demboyz/game/sourcecontext.h +++ b/demboyz/game/sourcecontext.h @@ -75,6 +75,7 @@ struct SourceGameContext void Start(); void Finish(bool dirty); + void End(); void StartCommandPacket(const CommandPacket& packet); void EndCommandPacket(const PacketTrailingBits& trailingBits); diff --git a/demboyz/io/demoreader.cpp b/demboyz/io/demoreader.cpp index c3b0b0c..65feec5 100644 --- a/demboyz/io/demoreader.cpp +++ b/demboyz/io/demoreader.cpp @@ -10,6 +10,9 @@ #include "sourcesdk/bitbuf.h" #include +static NetHandlers::NetDataStructArray s_netDataStructs; +static DemHandlers::DemDataStructArray s_demDataStructs; + PacketTrailingBits ParsePacket(uint8_t* packet, size_t length, SourceGameContext& context, const NetHandlers::NetDataStructArray& netDataStructs) @@ -38,13 +41,20 @@ PacketTrailingBits ParsePacket(uint8_t* packet, size_t length, 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) { - NetHandlers::NetDataStructArray netDataStructs; - DemHandlers::DemDataStructArray demDataStructs; - NetHandlers::CreateNetMsgStructs(netDataStructs); - DemHandlers::CreateDemMsgStructs(demDataStructs); - DemoFileReader reader(inputFp); { reader.ReadDemoHeader(context->header); @@ -59,7 +69,7 @@ bool DemoReader::ProcessDem(std::FILE* inputFp, SourceGameContext* context) do { 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); PacketTrailingBits trailingBits = PacketTrailingBits(); @@ -68,7 +78,7 @@ bool DemoReader::ProcessDem(std::FILE* inputFp, SourceGameContext* context) if (packet.cmd == dem_packet || packet.cmd == dem_signon) { Array 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) @@ -100,8 +110,5 @@ bool DemoReader::ProcessDem(std::FILE* inputFp, SourceGameContext* context) context->Finish(dirty); - DemHandlers::DestroyDemMsgStructs(demDataStructs); - NetHandlers::DestroyNetMsgStructs(netDataStructs); - - return !dirty; + return dirty; } diff --git a/demboyz/io/demoreader.h b/demboyz/io/demoreader.h index f6eddbc..52531dc 100644 --- a/demboyz/io/demoreader.h +++ b/demboyz/io/demoreader.h @@ -5,5 +5,7 @@ namespace DemoReader { + void Init(); + void DeInit(); bool ProcessDem(std::FILE* inputFp, SourceGameContext* context); } diff --git a/demboyz/io/voicewriter/voicedatawriter.cpp b/demboyz/io/voicewriter/voicedatawriter.cpp index 851e997..b425323 100644 --- a/demboyz/io/voicewriter/voicedatawriter.cpp +++ b/demboyz/io/voicewriter/voicedatawriter.cpp @@ -138,16 +138,26 @@ VoiceDataWriter::VoiceDataWriter(SourceGameContext* context, const char* outputP { } -void VoiceDataWriter::Start() +bool VoiceDataWriter::init() { int error = CELT_OK; const CeltConfig& config = sCeltConfigs[sQuality]; m_celtMode = celt_mode_create(config.sampleRate, config.frameSizeSamples, &error); assert(error == CELT_OK); assert(m_celtMode); + return error == CELT_OK; +} + +void VoiceDataWriter::Start() +{ + m_tickBase = m_curTick; } void VoiceDataWriter::Finish() +{ +} + +void VoiceDataWriter::End() { if(m_isSilenced) { @@ -175,7 +185,7 @@ void VoiceDataWriter::Finish() void VoiceDataWriter::StartCommandPacket(const CommandPacket& packet) { m_lastTick = m_curTick; - m_curTick = packet.tick; + m_curTick = m_tickBase + packet.tick; } void VoiceDataWriter::EndCommandPacket(const PacketTrailingBits& trailingBits) @@ -184,8 +194,8 @@ void VoiceDataWriter::EndCommandPacket(const PacketTrailingBits& trailingBits) if (m_curTick <= tickMargin) return; - // Skip silence if noone talks for at least 3 seconds - if((m_curTick - m_lastVoiceTick) / context->fTickRate > 3.0) + // Skip silence if noone talks for at least 1.5 seconds + if((m_curTick - m_lastVoiceTick) / context->fTickRate > 1.5) { if(!m_isSilenced) { diff --git a/demboyz/io/voicewriter/voicedatawriter.h b/demboyz/io/voicewriter/voicedatawriter.h index 3cd81b5..77904c7 100644 --- a/demboyz/io/voicewriter/voicedatawriter.h +++ b/demboyz/io/voicewriter/voicedatawriter.h @@ -54,9 +54,11 @@ class VoiceDataWriter { public: VoiceDataWriter(SourceGameContext *context, const char* outputPath); + bool init(); void Start(); void Finish(); + void End(); void StartCommandPacket(const CommandPacket& packet); void EndCommandPacket(const PacketTrailingBits& trailingBits); @@ -95,6 +97,7 @@ private: eCodec m_Codec = CODEC_NONE; public: + int32_t m_tickBase = 0; bool m_isSilenced = false; std::vector> m_silence; };