diff --git a/docs/cvars.md b/docs/cvars.md index e39d272f..f605c189 100644 --- a/docs/cvars.md +++ b/docs/cvars.md @@ -514,6 +514,7 @@ |sar_scrollspeed_y|210|Scroll speed HUD y offset.| |sar_seamshot_finder|0|Enables or disables seamshot finder overlay.| |sar_session|cmd|sar_session - prints the current tick of the server since it has loaded| +|sar_set_promo_items_state|cmd|sar_set_promo_items_state \... - enables coop promotional items on spawn.| |sar_show_entinp|0|Print all entity inputs to console.| |sar_skiptodemo|cmd|sar_skiptodemo \ - skip demos in demo queue to this demo| |sar_speedrun_autoreset_clear|cmd|sar_speedrun_autoreset_clear - stop using the autoreset file| diff --git a/src/Cheats.cpp b/src/Cheats.cpp index 885e5b9d..d50febd4 100644 --- a/src/Cheats.cpp +++ b/src/Cheats.cpp @@ -331,6 +331,7 @@ Memory::Patch *g_floorReportalPatch; Memory::Patch *g_coopLoadingDotsPatch; Memory::Patch *g_autoGrabPatchServer; Memory::Patch *g_autoGrabPatchClient; +Memory::Patch *g_promoFlagsPatch; void Cheats::Init() { sv_laser_cube_autoaim = Variable("sv_laser_cube_autoaim"); @@ -399,6 +400,14 @@ void Cheats::Init() { g_autoGrabPatchClient->Restore(); } + g_promoFlagsPatch = new Memory::Patch(); + auto portal2PromoFlags = Memory::Scan(MODULE("server"), Offsets::Portal2PromoFlagsSig, Offsets::Portal2PromoFlagsOff); + if (portal2PromoFlags) { + unsigned char promoFlagsByte = 0x00; + g_promoFlagsPatch->Execute(Memory::Deref(portal2PromoFlags), &promoFlagsByte, 1); // Note: Has to be active before map loads. + g_promoFlagsPatch->Restore(); + } + Variable::RegisterAll(); Command::RegisterAll(); } @@ -422,6 +431,8 @@ void Cheats::Shutdown() { SAFE_DELETE(g_autoGrabPatchServer); g_autoGrabPatchClient->Restore(); SAFE_DELETE(g_autoGrabPatchClient); + g_promoFlagsPatch->Restore(); + SAFE_DELETE(g_promoFlagsPatch); } @@ -589,3 +600,37 @@ void Cheats::CheckAutoGrab() { g_autoGrabPatchClient->Restore(); } } + +DECL_AUTO_COMMAND_COMPLETION(sar_set_promo_items_state, ({"skins", "helmet", "antenna"})) // TODO: Add support for autofilling multiple args. +CON_COMMAND_F_COMPLETION(sar_set_promo_items_state, "sar_set_promo_items_state ... - enables coop promotional items on spawn.\n", FCVAR_CHEAT, AUTOCOMPLETION_FUNCTION(sar_set_promo_items_state)) { + if (!g_promoFlagsPatch || !g_promoFlagsPatch->IsInit()) { + return console->Print("sar_set_promo_items_state is not available.\n"); + } + + if (args.ArgC() < 2) { + return console->Print(sar_set_promo_items_state.ThisPtr()->m_pszHelpString); + } + + unsigned char targetFlags = 0; + for (int i = 1; i < args.ArgC(); i++) { + if (strcasecmp(args[i], "off") == 0) { + g_promoFlagsPatch->Restore(); + return; + } + if (strcasecmp(args[i], "all") == 0) { + targetFlags = 0b111; + break; + } + if (strcasecmp(args[i], "skins") == 0) {; + targetFlags |= 0b001; + } else if (strcasecmp(args[i], "helmet") == 0) { + targetFlags |= 0b010; + } else if (strcasecmp(args[i], "antenna") == 0) { + targetFlags |= 0b100; + } else { + return console->Print(sar_set_promo_items_state.ThisPtr()->m_pszHelpString); + } + } + g_promoFlagsPatch->Restore(); + g_promoFlagsPatch->Execute(&targetFlags, 1); +} diff --git a/src/Cheats.hpp b/src/Cheats.hpp index 28141e5e..95a055c1 100644 --- a/src/Cheats.hpp +++ b/src/Cheats.hpp @@ -50,3 +50,5 @@ extern Variable hide_gun_when_holding; extern Variable r_flashlightbrightness; extern Command sar_togglewait; + +extern Memory::Patch *g_promoFlagsPatch; diff --git a/src/Modules/Server.cpp b/src/Modules/Server.cpp index 56ec981d..b687276a 100644 --- a/src/Modules/Server.cpp +++ b/src/Modules/Server.cpp @@ -285,6 +285,7 @@ DETOUR(Server::PlayerRunCommand, CUserCmd *cmd, void *moveHelper) { Cheats::AutoStrafe(slot, thisptr, cmd); Cheats::CheckFloorReportals(); + if (!sv_cheats.GetBool()) g_promoFlagsPatch->Restore(); // We only want to check this once per map load, to preserve the intended behavior. inputHud.SetInputInfo(slot, cmd->buttons, {cmd->sidemove, cmd->forwardmove, cmd->upmove}); diff --git a/src/Offsets/Portal 2 9568.hpp b/src/Offsets/Portal 2 9568.hpp index 1b4227e7..820e0826 100644 --- a/src/Offsets/Portal 2 9568.hpp +++ b/src/Offsets/Portal 2 9568.hpp @@ -537,6 +537,10 @@ SIGSCAN_DEFAULT(FloorReportalBranch, "75 7D 8B 8E C0 04 00 00", SIGSCAN_DEFAULT(CPortal_Player__PollForUseEntity_CheckMP, "74 ? ? ? 8B 82 ? ? ? ? FF D0 84 C0 74 ? 8B CE", "74 ? 8B 10 83 EC 0C 50 FF 92 88 00 00 00 83 C4 10 84 C0 ? ? ? ? ? ? ? ? ? ? ? ? ? 00 00") // "OnJump" xref -> CPortal_Player:PreThink -> CBasePlayer::PreThink -> CBasePlayer::ItemPreFrame -> CBasePlayer::PlayerUse -> CPortal_Player vtable offset -> CPortal_Player::PlayerUse -> Second function call from disassembly -> CPortal_Player::PollForUseEntity -> jz instruction +SIGSCAN_DEFAULT(Portal2PromoFlagsSig, "F6 05 ? ? ? ? 02 74 ? 8B CE", + "A1 ? ? ? ? A8 02") // "#P2_WearableType_Flag" xref -> CPortal_Player::GiveDefaultItems -> bitwise & -> portal2 promo flag +OFFSET_DEFAULT(Portal2PromoFlagsOff, 2, 1) + // VPhysics OFFSET_EMPTY(DestroyEnvironment) OFFSET_EMPTY(GetActiveEnvironmentByIndex) diff --git a/src/SourceAutoRecord.vcxproj b/src/SourceAutoRecord.vcxproj index 4c28a686..640a7c01 100644 --- a/src/SourceAutoRecord.vcxproj +++ b/src/SourceAutoRecord.vcxproj @@ -14,7 +14,7 @@ {3F6459A9-566E-4CAC-A412-C03FF51D67E9} SourceAutoRecord SourceAutoRecord - 10.0.19041.0 + 10.0 v142 DynamicLibrary false diff --git a/src/Utils/Memory.cpp b/src/Utils/Memory.cpp index 838d4bf9..0c8d9626 100644 --- a/src/Utils/Memory.cpp +++ b/src/Utils/Memory.cpp @@ -245,6 +245,7 @@ Memory::Patch::~Patch() { } bool Memory::Patch::Execute() { if (this->isPatched) return true; // already executed + if (!this->IsInit()) return false; unsigned char *tmpPatch = new unsigned char[this->size]; // We create another patch, because this->patch is gonna be deleted memcpy(tmpPatch, this->patch, this->size); diff --git a/src/Utils/Memory.hpp b/src/Utils/Memory.hpp index b281f9a3..593506e5 100644 --- a/src/Utils/Memory.hpp +++ b/src/Utils/Memory.hpp @@ -50,6 +50,13 @@ namespace Memory { bool Execute(uintptr_t location, unsigned char (&bytes)[size]) { return Execute(location, bytes, size); } + bool Execute(unsigned char *bytes, size_t size) { + return Execute(location, bytes, size); + } + template + bool Execute(unsigned char (&bytes)[size]) { + return Execute(location, bytes, size); + } bool Restore(); bool IsPatched(); bool IsInit();