Skip to content
Merged
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
25 changes: 13 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,19 @@ For features and installation instructions, please visit [the website].

## Support

| Game | Windows | Linux |
|-------------------------------------------------------------------------|---------|-------|
| [Portal 2](https://store.steampowered.com/app/620) | ✔ | ✔ |
| [Aperture Tag](https://store.steampowered.com/app/280740) | ✔ | [➖](https://wiki.portal2.sr/Aperture_Tag#Linux) |
| [Portal Stories: Mel](https://store.steampowered.com/app/317400) | ✔ | ✔ |
| [Thinking with Time Machine](https://store.steampowered.com/app/286080) | ✔ | ✔ |
| [Portal Reloaded](https://store.steampowered.com/app/1255980) | ✔ | ✔ |
| [INFRA](https://store.steampowered.com/app/251110) | ✔ | ➖ |
| [The Beginner's Guide](https://store.steampowered.com/app/303210) | ✔ | ✔ |
| [The Stanley Parable](https://store.steampowered.com/app/221910) | ✔ | ✔ |
| [The Cleaning Game](https://store.steampowered.com/app/3281900) | ✔ | ➖ |
| Divinity Chapter 2 *(closed beta)* | ✔ | ❓ |
| Game | Windows | Linux |
|----------------------------------------------------------------------------|---------|-------|
| [Portal 2 (Steam)](https://store.steampowered.com/app/620) | ✔ | ✔ |
| [Portal 2 (4554)](https://sourceunpack.gameabusefastcomplete.com/#p2-4554) | ✔ | ➖ |
| [Aperture Tag](https://store.steampowered.com/app/280740) | ✔ | [➖](https://wiki.portal2.sr/Aperture_Tag#Linux) |
| [Portal Stories: Mel](https://store.steampowered.com/app/317400) | ✔ | ✔ |
| [Thinking with Time Machine](https://store.steampowered.com/app/286080) | ✔ | ❌ |
| [Portal Reloaded](https://store.steampowered.com/app/1255980) | ✔ | ✔ |
| [INFRA](https://store.steampowered.com/app/251110) | ✔ | ➖ |
| [The Beginner's Guide](https://store.steampowered.com/app/303210) | ✔ | ✔ |
| [The Stanley Parable](https://store.steampowered.com/app/221910) | ✔ | ✔ |
| [The Cleaning Game](https://store.steampowered.com/app/3281900) | ✔ | ➖ |
| Divinity Chapter 2 *(closed beta)* | ✔ | ❓ |

If you're playing a game with no Linux support, you can use Proton to run it.

Expand Down
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. Requires sv_cheats 1 to enable.|
|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
9 changes: 7 additions & 2 deletions docs/web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,15 @@ <h1 id="support">Game support</h1>
<th>Linux</th>
</tr>
<tr>
<td><a href="https://store.steampowered.com/app/620" target="_blank">Portal 2</a></td>
<td><a href="https://store.steampowered.com/app/620" target="_blank">Portal 2 (Steam)</a></td>
<td>✔</td>
<td>✔</td>
</tr>
<tr>
<td><a href="https://store.steampowered.com/app/620" target="_blank">Portal 2 (4554)</a></td>
<td>✔</td>
<td>➖</td>
</tr>
<tr>
<td><a href="https://store.steampowered.com/app/280740" target="_blank">Aperture Tag</a></td>
<td>✔</td>
Expand All @@ -145,7 +150,7 @@ <h1 id="support">Game support</h1>
<tr>
<td><a href="https://store.steampowered.com/app/286080" target="_blank">Thinking with Time Machine</a></td>
<td>✔</td>
<td></td>
<td></td>
</tr>
<tr>
<td><a href="https://store.steampowered.com/app/1255980" target="_blank">Portal Reloaded</a></td>
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() && !sv_cheats.GetBool()) {
console->Print("sar_demo_modelcache_clear_protected_flags requires sv_cheats 1.\n");
sar_demo_modelcache_clear_protected_flags.SetValue(0);
}
}

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

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

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() && sv_cheats.GetBool()) {
ClearProtectedModelFlags();
}
}
3 changes: 3 additions & 0 deletions src/Offsets/Portal 2 8151.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ 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")

// 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 Down
14 changes: 14 additions & 0 deletions src/Offsets/Portal 2 9568.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,20 @@ SIGSCAN_DEFAULT(InterpolateDemoCommand, "55 8B EC 83 EC 10 56 8B F1 8B 4D 10 57
"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
SIGSCAN_DEFAULT(UpdateLeaderboardData, "55 8B EC 83 EC 08 53 8B D9 8B 03 8B 50 08",
"55 89 E5 57 56 53 83 EC 2C 8B 45 08 8B 5D 0C")
Expand Down
Loading