Skip to content

Commit af291e9

Browse files
authored
feat: runtime vscript checksums (#361)
1 parent 7675753 commit af291e9

15 files changed

Lines changed: 153 additions & 1 deletion

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ DEPS=$(OBJS:%.o=%.d)
1515

1616
WARNINGS=-Wall -Wno-parentheses -Wno-unknown-pragmas -Wno-delete-non-virtual-dtor -Wno-overloaded-virtual
1717
CXXFLAGS=-std=c++17 -m32 $(WARNINGS) -I$(SDIR) -fPIC -D_GNU_SOURCE -Ilib/ffmpeg/include -Ilib/SFML/include -Ilib/curl/include -Ilib/discord-rpc/include -DSFML_STATIC -DCURL_STATICLIB
18-
LDFLAGS=-m32 -shared -lstdc++fs -Llib/ffmpeg/lib/linux -lavformat -lavcodec -lavutil -lswscale -lswresample -lx264 -lx265 -lvorbis -lvorbisenc -lvorbisfile -logg -lopus -lvpx -Llib/SFML/lib/linux -lsfml -Llib/curl/lib/linux -lcurl -lssl -lcrypto -lnghttp2 -Llib/discord-rpc/lib/linux -ldiscord-rpc
18+
LDFLAGS=-m32 -shared -lstdc++fs -Llib/ffmpeg/lib/linux -lavformat -lavcodec -lavutil -lswscale -lswresample -lx264 -lx265 -lvorbis -lvorbisenc -lvorbisfile -logg -lopus -lvpx -Llib/SFML/lib/linux -lsfml -Llib/curl/lib/linux -lcurl -lssl -lcrypto -lnghttp2 -Llib/discord-rpc/lib/linux -ldiscord-rpc -static-libstdc++ -static-libgcc
1919

2020
# Import config.mk, which can be used for optional config
2121
-include config.mk

src/Checksum.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
#define READ_LE32(arr, i) \
2828
(((uint32_t)arr[i + 0] << 0) | ((uint32_t)arr[i + 1] << 8) | ((uint32_t)arr[i + 2] << 16) | ((uint32_t)arr[i + 3] << 24))
2929

30+
static constexpr uint8_t SAR_MSG_VSCRIPT_RUNTIME_CHECKSUM = 0x14;
31+
3032
// clang-format off
3133
static const uint32_t crcTable[256] = {
3234
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
@@ -560,6 +562,40 @@ static void addVpkInternalChecksum(const VpkInternalData &vpk) {
560562
engine->demorecorder->RecordData(data.data(), data.size());
561563
}
562564

565+
static size_t BoundedCStringLen(const char *str, size_t maxLen) {
566+
if (!str) return 0;
567+
size_t len = 0;
568+
while (len < maxLen && str[len]) ++len;
569+
return len;
570+
}
571+
572+
void RecordRuntimeVscriptChecksum(const char *scriptName, const char *scriptData) {
573+
if (!scriptName || !*scriptName || !scriptData || !*scriptData) return;
574+
575+
// Avoid an unbounded C-string search in case file data is not null-terminated.
576+
constexpr size_t MAX_SCRIPT_SIZE = 8 * 1024 * 1024;
577+
size_t scriptLen = BoundedCStringLen(scriptData, MAX_SCRIPT_SIZE);
578+
if (scriptLen == 0) return;
579+
580+
uint32_t sum = 0;
581+
if (scriptLen < MAX_SCRIPT_SIZE) {
582+
sum = crc32(scriptData, scriptLen);
583+
}
584+
585+
if (engine->demorecorder->isRecordingDemo && engine->demorecorder->GetTick() >= 0) {
586+
size_t nameLen = strlen(scriptName);
587+
size_t bufLen = nameLen + 6;
588+
auto *buf = new uint8_t[bufLen];
589+
buf[0] = SAR_MSG_VSCRIPT_RUNTIME_CHECKSUM;
590+
*reinterpret_cast<uint32_t *>(buf + 1) = sum;
591+
strcpy(reinterpret_cast<char *>(buf + 5), scriptName);
592+
engine->demorecorder->RecordData(buf, bufLen);
593+
delete[] buf;
594+
} else {
595+
engine->demorecorder->queuedVScriptChecksums.emplace_back(scriptName, sum);
596+
}
597+
}
598+
563599
void AddDemoVpkChecksums() {
564600
if (g_vpkThread.joinable()) g_vpkThread.join();
565601

src/Checksum.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
bool AddDemoChecksum(const char *filename);
77
void AddDemoFileChecksums();
88
void AddDemoVpkChecksums();
9+
void RecordRuntimeVscriptChecksum(const char *scriptName, const char *scriptData);

src/Modules.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@
1212
#include "Modules/Tier1.hpp"
1313
#include "Modules/VGui.hpp"
1414
#include "Modules/VPhysics.hpp"
15+
#include "Modules/VScript.hpp"

src/Modules/Client.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ void Client::MultiColorChat(const std::vector<std::pair<Color, std::string>> &co
204204
}
205205

206206
void Client::ShowLocator(Vector position, Vector normal, Color color) {
207+
if (!PrecacheParticleSystem || !DispatchParticleEffect) return;
207208
Vector colorAsVector = {(float)color.r, (float)color.g, (float)color.b};
208209
QAngle angles;
209210

src/Modules/EngineDemoPlayer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ std::string EngineDemoPlayer::GetLevelName() {
174174
// 0x11: VPK internal checksums
175175
// 0x12: incomplete speedrun summary
176176
// 0x13: speedrun identifier
177+
// 0x14: runtime vscript checksum
177178
void EngineDemoPlayer::CustomDemoData(char *data, size_t length) {
178179
if (data[0] == 0x03 || data[0] == 0x04) { // Entity input data
179180
std::optional<int> slot;

src/Modules/EngineDemoRecorder.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,33 @@ static void RecordQueuedCommands() {
103103
engine->demorecorder->queuedCommands.clear();
104104
}
105105

106+
static void RecordQueuedVScriptChecksums() {
107+
if (!engine->demorecorder->isRecordingDemo) return;
108+
if (!engine->demorecorder->customDataReady) return;
109+
if (engine->demorecorder->GetTick() < 0) return;
110+
111+
for (auto &queuedChecksum : engine->demorecorder->queuedVScriptChecksums) {
112+
size_t nameLen = queuedChecksum.first.size();
113+
size_t bufLen = nameLen + 6;
114+
auto *buf = new uint8_t[bufLen];
115+
buf[0] = 0x14;
116+
*reinterpret_cast<uint32_t *>(buf + 1) = queuedChecksum.second;
117+
strcpy(reinterpret_cast<char *>(buf + 5), queuedChecksum.first.c_str());
118+
engine->demorecorder->RecordData(buf, bufLen);
119+
delete[] buf;
120+
}
121+
engine->demorecorder->queuedVScriptChecksums.clear();
122+
}
123+
124+
ON_EVENT(PRE_TICK) {
125+
if (!engine->demorecorder->queuedVScriptChecksums.empty()) {
126+
RecordQueuedVScriptChecksums();
127+
}
128+
}
129+
106130
ON_EVENT(SESSION_END) {
131+
engine->demorecorder->queuedVScriptChecksums.clear();
132+
107133
if (*engine->demorecorder->m_bRecording && sar_autorecord.GetInt() == -1) {
108134
engine->demorecorder->Stop();
109135
}
@@ -172,6 +198,7 @@ DETOUR(EngineDemoRecorder::SetSignonState, int state) {
172198
RecordTimestamp();
173199
SpeedrunTimer::WriteIdToDemo(); // Write speedrun ID to every demo segment
174200
RecordQueuedCommands();
201+
RecordQueuedVScriptChecksums();
175202
SpeedrunTimer::RecordIncompleteSummary();
176203
engine->ExecuteCommand("echo \"SAR " SAR_VERSION " (Built " SAR_BUILT ")\"", true);
177204
AddDemoFileChecksums();

src/Modules/EngineDemoRecorder.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include "Utils.hpp"
66

77
#include <string>
8+
#include <utility>
9+
#include <vector>
810

911
// Ticks before demo autostop
1012
#define DEMO_AUTOSTOP_DELAY 15
@@ -28,6 +30,7 @@ class EngineDemoRecorder : public Module {
2830
int autorecordStartNum = 1;
2931

3032
std::vector<std::string> queuedCommands = {};
33+
std::vector<std::pair<std::string, uint32_t>> queuedVScriptChecksums = {};
3134

3235
char coopRadialMenuLastPos[8];
3336

src/Modules/VScript.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#include "VScript.hpp"
2+
3+
#include "Checksum.hpp"
4+
#include "Game.hpp"
5+
#include "Hook.hpp"
6+
#include "Offsets.hpp"
7+
#include "SAR.hpp"
8+
9+
REDECL(VScript::CompileScript);
10+
11+
extern Hook g_VScriptCompileScriptHook;
12+
DETOUR_T(int, VScript::CompileScript, const char *scriptData, const char *scriptName) {
13+
if (scriptName && *scriptName) {
14+
RecordRuntimeVscriptChecksum(scriptName, scriptData);
15+
}
16+
17+
g_VScriptCompileScriptHook.Disable();
18+
auto ret = VScript::CompileScript(thisptr, scriptData, scriptName);
19+
g_VScriptCompileScriptHook.Enable();
20+
return ret;
21+
}
22+
Hook g_VScriptCompileScriptHook(&VScript::CompileScript_Hook);
23+
24+
bool VScript::Init() {
25+
VScript::CompileScript = (VScript::_CompileScript)Memory::Scan(this->Name(), Offsets::VScript_CompileScript);
26+
if (!VScript::CompileScript) {
27+
return false;
28+
}
29+
30+
g_VScriptCompileScriptHook.SetFunc(VScript::CompileScript);
31+
return this->hasLoaded = true;
32+
}
33+
34+
void VScript::Shutdown() {
35+
}
36+
37+
VScript *vscript;

src/Modules/VScript.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#pragma once
2+
3+
#include "Module.hpp"
4+
#include "Utils.hpp"
5+
6+
class VScript : public Module {
7+
public:
8+
// CVScriptGameSystem::CompileScript
9+
DECL_DETOUR_T(int, CompileScript, const char *scriptData, const char *scriptName);
10+
11+
bool Init() override;
12+
void Shutdown() override;
13+
const char *Name() override { return MODULE("vscript"); }
14+
};
15+
16+
extern VScript *vscript;

0 commit comments

Comments
 (0)