add support for split demo files (UNLOZE)
This commit is contained in:
parent
3b723a64e0
commit
d4a0d51bfc
@ -8,13 +8,19 @@
|
|||||||
|
|
||||||
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();
|
||||||
|
SourceGameContext *context = nullptr;
|
||||||
|
|
||||||
|
bool error = false;
|
||||||
|
for (int i = 1; i < argc; i++)
|
||||||
|
{
|
||||||
|
std::filesystem::path inputFile(argv[i]);
|
||||||
FILE* inputFp = fopen(inputFile.c_str(), "rb");
|
FILE* inputFp = fopen(inputFile.c_str(), "rb");
|
||||||
if (!inputFp)
|
if (!inputFp)
|
||||||
{
|
{
|
||||||
@ -22,18 +28,29 @@ int main(const int argc, const char* argv[])
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::path outputDir = inputFile.filename().replace_extension();
|
static std::filesystem::path outputDir = inputFile.filename().replace_extension();
|
||||||
std::filesystem::path outputDirVoice = outputDir.string() + "/voice";
|
static std::filesystem::path outputDirVoice = outputDir.string() + "/voice";
|
||||||
|
if (i == 1)
|
||||||
|
{
|
||||||
std::filesystem::create_directory(outputDir);
|
std::filesystem::create_directory(outputDir);
|
||||||
std::filesystem::create_directory(outputDirVoice);
|
std::filesystem::create_directory(outputDirVoice);
|
||||||
|
|
||||||
SourceGameContext context = SourceGameContext(outputDir, outputDirVoice);
|
context = new SourceGameContext(outputDir, outputDirVoice);
|
||||||
if (!context.init())
|
if(!context->init())
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
bool error = !DemoReader::ProcessDem(inputFp, &context);
|
bool dirty = DemoReader::ProcessDem(inputFp, context);
|
||||||
|
if(dirty)
|
||||||
|
error = true;
|
||||||
|
|
||||||
fclose(inputFp);
|
fclose(inputFp);
|
||||||
|
}
|
||||||
|
|
||||||
|
context->End();
|
||||||
|
delete context;
|
||||||
|
|
||||||
|
DemoReader::DeInit();
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,15 @@ Logic::~Logic()
|
|||||||
|
|
||||||
void Logic::Start()
|
void Logic::Start()
|
||||||
{
|
{
|
||||||
|
tickBase = curTick;
|
||||||
|
if(curTick)
|
||||||
|
{
|
||||||
|
data["demoheader"]["playback_time"] = data["demoheader"]["playback_time"].get<float>() + context->header.playback_time;
|
||||||
|
data["demoheader"]["playback_ticks"] = data["demoheader"]["playback_ticks"].get<int32_t>() + context->header.playback_ticks;
|
||||||
|
data["demoheader"]["playback_frames"] = data["demoheader"]["playback_frames"].get<int32_t>() + context->header.playback_frames;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
data["demoheader"] = json({
|
data["demoheader"] = json({
|
||||||
{"demofilestamp", context->header.demofilestamp},
|
{"demofilestamp", context->header.demofilestamp},
|
||||||
{"demoprotocol", context->header.demoprotocol},
|
{"demoprotocol", context->header.demoprotocol},
|
||||||
@ -38,8 +47,9 @@ void Logic::Start()
|
|||||||
{"playback_frames", context->header.playback_frames},
|
{"playback_frames", context->header.playback_frames},
|
||||||
{"signonlength", context->header.signonlength}
|
{"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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user