Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/cvars.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@
|sar_demo_clean_start|0|Attempts to minimize visual interpolation of some elements (like post-processing or lighting) when demo playback begins.|
|sar_demo_clean_start_tonemap|0|Overrides initial tonemap scalar value used in auto-exposure.<br>Setting it to 0 will attempt to skip over to target value for several ticks.|
|sar_demo_clean_start_tonemap_sample|cmd|sar_demo_clean_start_tonemap_sample [tick] - samples tonemap scale from current demo at given tick and stores it in "sar_demo_clean_start_tonemap" variable. If no tick is given, sampling will happen when `__END__` is seen in demo playback.|
|sar_demo_modelcache_clear_protected_flags|0|Fix demo model-cache growth by clearing stale CModelLoader protected flags on demo stop.|
|sar_demo_overwrite_bak|0|Rename demos to (name)_bak if they would be overwritten by recording|
|sar_demo_portal_interp_fix|1|Fix eye interpolation through portals in demo playback.|
|sar_demo_remove_broken|1|Whether to remove broken frames from demo playback|
Expand Down
84 changes: 84 additions & 0 deletions src/Features/Demo/ModelCacheTools.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#include "Event.hpp"
#include "Modules/Console.hpp"
#include "Modules/Server.hpp"
#include "Offsets.hpp"
#include "Utils.hpp"
#include "Utils/Memory.hpp"
#include "Variable.hpp"

namespace {
constexpr unsigned int MODEL_FLAG_PROTECTED_MASK = 0x7E;
constexpr unsigned int MODEL_FLAG_SKIP_PROTECTED_CLEAR = 0x40;
constexpr unsigned int MAX_REASONABLE_MODEL_COUNT = 16384;

DECL_CVAR_CALLBACK(sar_demo_modelcache_clear_protected_flags);

Variable sar_demo_modelcache_clear_protected_flags(
"sar_demo_modelcache_clear_protected_flags",
"0",
"Fix demo model-cache growth by clearing stale CModelLoader protected flags on demo stop. Requires sv_cheats 1 to enable.\n",
FCVAR_NONE,
sar_demo_modelcache_clear_protected_flags_callback);

DECL_CVAR_CALLBACK(sar_demo_modelcache_clear_protected_flags) {
if (sar_demo_modelcache_clear_protected_flags.GetBool() && flOldValue == 0.0f && !sv_cheats.GetBool()) {
console->Print("sar_demo_modelcache_clear_protected_flags requires sv_cheats 1.\n");
sar_demo_modelcache_clear_protected_flags.SetValue(pOldValue ? pOldValue : "0");
}
}

void *GetModelLoader() {
static uintptr_t global = 0;
if (!global) {
auto site = Memory::Scan(MODULE("engine"), Offsets::CModelLoaderModelPrecache);
if (!site) return nullptr;

global = Memory::Deref<uintptr_t>(site + Offsets::CModelLoaderModelPrecacheGlobal);
}

return Memory::Deref<void *>(global);
}

void ClearProtectedModelFlags() {
auto modelLoader = reinterpret_cast<uintptr_t>(GetModelLoader());
if (!modelLoader) {
static bool warned = false;
if (!warned) {
warned = true;
console->Warning("SAR: Failed to find CModelLoader for repeated-demo model-cache cleanup.\n");
}
return;
}

auto entries = *reinterpret_cast<uintptr_t *>(modelLoader + Offsets::CModelLoaderEntryArray);
auto count = *reinterpret_cast<unsigned short *>(modelLoader + Offsets::CModelLoaderEntryCount);
if (!entries || count > MAX_REASONABLE_MODEL_COUNT) {
static bool warned = false;
if (!warned) {
warned = true;
console->Warning(
"SAR: Invalid CModelLoader state (loader=%p entries=%p count=%u).\n",
reinterpret_cast<void *>(modelLoader),
reinterpret_cast<void *>(entries),
count);
}
return;
}

for (unsigned int i = 0; i < count; ++i) {
auto model = *reinterpret_cast<uintptr_t *>(entries + i * Offsets::CModelLoaderEntryStride + Offsets::CModelLoaderEntryModel);
if (!model) continue;

auto flags = reinterpret_cast<unsigned int *>(model + Offsets::CModelLoaderModelFlags);
if ((*flags & MODEL_FLAG_SKIP_PROTECTED_CLEAR) != 0) continue;

*flags &= ~MODEL_FLAG_PROTECTED_MASK;
}
}
} // namespace

ON_EVENT(DEMO_STOP) {
if (sar_demo_modelcache_clear_protected_flags.GetBool()) {
ClearProtectedModelFlags();
}
}
9 changes: 9 additions & 0 deletions src/Offsets/Portal 2 5723.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,12 @@ OFFSET_LINUX(DrawPortalSpBranchOff, 0x15)
SIGSCAN_LINUX(DrawPortalGhost, "55 89 E5 57 56 53 83 EC 5C A1 ? ? ? ? 8B 40")
SIGSCAN_LINUX(DrawPortalGhostSpBranch, "0F 84 ? ? ? ? FF 90 ? ? ? ? 80 BB ? ? ? ? 01")
SIGSCAN_LINUX(GetChapterProgress, "55 89 E5 57 56 53 83 EC 2C 8B 7D 08 E8 ? ? ? ? 8B 10 C7")
SIGSCAN_LINUX(DispatchParticleEffect,"")
SIGSCAN_LINUX(PrecacheParticleSystem, "")
SIGSCAN_LINUX(GetCurrentTonemappingSystem, "")
SIGSCAN_LINUX(ResetToneMapping, "")
SIGSCAN_LINUX(LoadingProgress__SetupControlStatesInstruction, "")

// Server
SIGSCAN_LINUX(FloorReportalBranch,"")
SIGSCAN_LINUX(CPortal_Player__PollForUseEntity_CheckMP, "")
17 changes: 17 additions & 0 deletions src/Offsets/Portal 2 8151.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ SIGSCAN_LINUX(AddShadowToReceiver, "55 89 E5 57 56 53 83 EC ? 8B 45 ? 8B 4D ? 8B
SIGSCAN_LINUX(UTIL_Portal_Color, "55 89 E5 56 53 83 EC 10 8B 75 ? 8B 5D ? 85 F6 0F 84")
SIGSCAN_LINUX(UTIL_Portal_Color_Particles, "55 89 E5 53 83 EC 14 A1 ? ? ? ? 8B 5D ? 8B 10 89 04 24 FF 92 ? ? ? ? 84 C0 75 ? 83 7D ? 01")
SIGSCAN_LINUX(GetChapterProgress, "55 89 E5 57 56 53 83 EC 2C 8B 5D 08 E8 ? ? ? ? 8B 10")
SIGSCAN_LINUX(DispatchParticleEffect, "")
SIGSCAN_LINUX(PrecacheParticleSystem, "")
SIGSCAN_LINUX(GetCurrentTonemappingSystem, "")
SIGSCAN_LINUX(ResetToneMapping, "")
SIGSCAN_LINUX(LoadingProgress__SetupControlStatesInstruction, "")

// Engine
SIGSCAN_LINUX(Host_AccumulateTime, "55 89 E5 83 EC 28 F3 0F 10 05 ? ? ? ? A1 ? ? ? ? F3 0F 58 45 08 F3 0F 11 05 ? ? ? ? 8B 10 89 04 24 FF 52 24")
Expand All @@ -82,6 +87,15 @@ SIGSCAN_LINUX(InsertCommand, "55 89 E5 57 56 53 83 EC 1C 8B 75 ? 8B 5D ? 81 FE F
// EngineDemoPlayer
SIGSCAN_LINUX(InterpolateDemoCommand, "55 31 C9 89 E5 57 56 53 83 EC 3C 89 4D F0 8B 45 08 8B 4D 14 8B 80 B0 05 00 00 89 45 B8 8B 45 14 83 C0 04 89 45 D0")

// CModelLoader
SIGSCAN_LINUX(CModelLoaderModelPrecache, "A1 ? ? ? ? 8B 8D ? ? ? ? 8B 10 C7 44 24 08 04 00 00 00 89 4C 24 04 89 04 24 FF 52 1C")
OFFSET_LINUX(CModelLoaderModelPrecacheGlobal, 1)
OFFSET_LINUX(CModelLoaderEntryArray, 0x8)
OFFSET_LINUX(CModelLoaderEntryCount, 0x16)
OFFSET_LINUX(CModelLoaderEntryStride, 0x10)
OFFSET_LINUX(CModelLoaderEntryModel, 0xC)
OFFSET_LINUX(CModelLoaderModelFlags, 0x108)

// MaterialSystem
SIGSCAN_LINUX(KeyValues_SetString, "55 89 E5 53 83 EC ? 8B 45 ? C7 44 24 ? ? ? ? ? 8B 5D ? 89 44 24 ? 8B 45 ? 89 04 24 E8 ? ? ? ? 85 C0 74 ? 89 5D")

Expand All @@ -98,5 +112,8 @@ SIGSCAN_LINUX(UTIL_GetCommandClientIndex, "A1 ? ? ? ? 55 89 E5 5D 83 C0 01 C3")
SIGSCAN_LINUX(CheckStuck_FloatTime, "E8 ? ? ? ? 8B 43 04 DD 9D ? ? ? ? F2 0F 10 B5 ? ? ? ? 8B 50 24 66 0F 14 F6 66 0F 5A CE 85 D2")
SIGSCAN_DEFAULT(aircontrol_fling_speedSig, "0F 2F 25 ? ? ? ? 0F 28 F0",
"0F 2E 05 ? ? ? ? 0F 86 ? ? ? ? 0F 2E 25")
SIGSCAN_DEFAULT(Portal2PromoFlagsSig, "", "")
SIGSCAN_LINUX(FloorReportalBranch, "")
SIGSCAN_LINUX(CPortal_Player__PollForUseEntity_CheckMP, "")

// clang-format on
16 changes: 15 additions & 1 deletion src/Offsets/Portal 2 9568.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,21 @@ OFFSET_DEFAULT(StartupDemoFile_HeaderName, 212, 184)

// EngineDemoPlayer
SIGSCAN_DEFAULT(InterpolateDemoCommand, "55 8B EC 83 EC 10 56 8B F1 8B 4D 10 57 8B BE B4 05 00 00 83 C1 04 89 75 F4 89 7D F0 E8 ? ? ? ? 8B 4D 14 83 C1 04",
"55 57 56 53 83 EC 10 8B 44 24 24 8B 5C 24 2C 8B 88 B0 05 00 00 8B 44 24 30 8D 70 04 8D 90 9C 00 00 00 89 F0 F3 0F 10 40 04")
"55 57 56 53 83 EC 10 8B 44 24 24 8B 5C 24 2C 8B 88 B0 05 00 00 8B 44 24 30 8D 70 04 8D 90 9C 00 00 00 89 F0 F3 0F 10 40 04")


// CModelLoader
// win: "modelprecache" xref -> client string-table update callback -> CModelLoader vtable +0x1C call with flag 4; global immediate is g_pModelLoader
// linux: "CClientState::ConsistencyCheck" xref -> model consistency type 3 block -> CModelLoader vtable +0x1C call with flag 4; global immediate is g_pModelLoader
SIGSCAN_DEFAULT(CModelLoaderModelPrecache,
"8B 0D ? ? ? ? 8B 11 6A 04 50 8B 42 1C FF D0 50 EB 02 6A 00",
"A1 ? ? ? ? 83 EC 04 8B 10 6A 04 FF B5 ? ? ? ? 50 FF 52 1C 89 85 ? ? ? ? 83 C4 10 85 C0")
OFFSET_DEFAULT(CModelLoaderModelPrecacheGlobal, 2, 1)
OFFSET_DEFAULT(CModelLoaderEntryArray, 0x8, 0x8) // "CModelLoader::FindModel: NULL name" xref -> successful lookup path reads [this+8] + index*0x10 + 0xC
OFFSET_DEFAULT(CModelLoaderEntryCount, 0x16, 0x16) // same CModelLoader::FindModel tree/list state; active count is this+0x16
OFFSET_DEFAULT(CModelLoaderEntryStride, 0x10, 0x10) // same CModelLoader::FindModel lookup path; entry nodes are 0x10 bytes
OFFSET_DEFAULT(CModelLoaderEntryModel, 0xC, 0xC) // same CModelLoader::FindModel lookup path; entry+0xC is model_t *
OFFSET_DEFAULT(CModelLoaderModelFlags, 0x108, 0x108) // CModelLoader vtable +0x1C target ORs caller flags into model_t+0x108


// Matchmaking
Expand Down
Loading