From 348719ae46f316a236d1655391680b46edd4e08f Mon Sep 17 00:00:00 2001 From: nullifiedcat Date: Fri, 5 May 2017 22:36:00 +0300 Subject: [PATCH] S K I N C H :a: :ng: E R wow! --- src/hacks/SkinChanger.cpp | 241 ++++++++++++++++++++++++++++++-------- src/hacks/SkinChanger.hpp | 29 ++++- src/netvars.cpp | 2 +- 3 files changed, 215 insertions(+), 57 deletions(-) diff --git a/src/hacks/SkinChanger.cpp b/src/hacks/SkinChanger.cpp index 16715425..0850bc10 100644 --- a/src/hacks/SkinChanger.cpp +++ b/src/hacks/SkinChanger.cpp @@ -23,7 +23,6 @@ ItemSchemaPtr_t GetItemSchema(void) { if (!ItemSystem) { ItemSystem = (ItemSystem_t)gSignatures.GetClientSignature((char*)sig_GetItemSchema); } - logging::Info("ItemSystem: 0x%08x 0x%08x", ItemSystem, ItemSystem()); return (void*)((uint32_t)(ItemSystem()) + 4); } @@ -32,6 +31,17 @@ CAttribute::CAttribute(uint16_t iAttributeDefinitionIndex, float flValue) { value = flValue; } + +float CAttributeList::GetAttribute(int defindex) { + for (int i = 0; i < m_Attributes.Count(); i++) { + const auto& a = m_Attributes[i]; + if (a.defidx == defindex) { + return a.value; + } + } + return 0.0f; +} + void CAttributeList::RemoveAttribute(int index) { for (int i = 0; i < m_Attributes.Count(); i++) { const auto& a = m_Attributes[i]; @@ -45,11 +55,10 @@ void CAttributeList::RemoveAttribute(int index) { CAttributeList::CAttributeList() {} void CAttributeList::SetAttribute(int index, float value) { - ItemSchemaPtr_t schema = GetItemSchema(); - logging::Info("Schema: 0x%08x", schema); + static ItemSchemaPtr_t schema = GetItemSchema(); AttributeDefinitionPtr_t attrib = GetAttributeDefinitionFn(schema, index); - logging::Info("Attrib: 0x%08x", attrib); SetRuntimeAttributeValueFn(this, attrib, value); + // The code below actually is unused now - but I'll keep it just in case! // Let's check if attribute exists already. We don't want dupes. /*for (int i = 0; i < m_Attributes.Count(); i++) { auto& a = m_Attributes[i]; @@ -62,33 +71,64 @@ void CAttributeList::SetAttribute(int index, float value) { if (m_Attributes.Count() > 14) return; - //m_Attributes.m_Memory.m_nGrowSize = -1; - //logging::Info("0x%08x 0x%08x 0x%08x", m_Attributes.m_Memory.m_nAllocationCount, m_Attributes.m_Memory.m_nGrowSize, m_Attributes.m_Memory.m_pMemory); - //m_Attributes.m_Memory.SetExternalBuffer(m_Attributes.m_Memory.Base(), 15); - CAttribute attr( index, value ); - m_Attributes.AddToTail(attr);*/ + m_Attributes.AddToTail({ index, value }); + */ } static CatVar enabled(CV_SWITCH, "skinchanger", "0", "Skin Changer"); static CatCommand set_attr("skinchanger_set", "Set Attribute", [](const CCommand& args) { unsigned attrid = strtoul(args.Arg(1), nullptr, 10); - unsigned attrv = strtoul(args.Arg(2), nullptr, 10); + float attrv = strtof(args.Arg(2), nullptr); GetModifier(CE_INT(LOCAL_W, netvar.iItemDefinitionIndex)).Set(attrid, attrv); - InvalidateCookies(); + InvalidateCookie(); +}); +static CatCommand remove_attr("skinchanger_remove", "Remove attribute", [](const CCommand& args) { + unsigned attrid = strtoul(args.Arg(1), nullptr, 10); + GetModifier(CE_INT(LOCAL_W, netvar.iItemDefinitionIndex)).Remove(attrid); + InvalidateCookie(); }); static CatCommand set_redirect("skinchanger_redirect", "Set Redirect", [](const CCommand& args) { unsigned redirect = strtoul(args.Arg(1), nullptr, 10); GetModifier(CE_INT(LOCAL_W, netvar.iItemDefinitionIndex)).defidx_redirect = redirect; - InvalidateCookies(); + InvalidateCookie(); }); static CatCommand dump_attrs("skinchanger_debug_attrs", "Dump attributes", []() { - CAttributeList* list = CE_VAR(LOCAL_W, netvar.AttributeList, CAttributeList*); + CAttributeList* list = (CAttributeList*)((uintptr_t)(RAW_ENT(LOCAL_W)) + netvar.AttributeList); logging::Info("ATTRIBUTE LIST: %i", list->m_Attributes.Size()); - for (int i = 0; i < 15; i++) { + for (int i = 0; i < list->m_Attributes.Size(); i++) { logging::Info("%i %.2f", list->m_Attributes[i].defidx, list->m_Attributes[i].value); } }); -static CatCommand invalidate_cookies("skinchanger_invalidate_cookies", "Invalidate Cookies", InvalidateCookies); + +static CatCommand list_redirects("skinchanger_redirect_list", "Dump redirects", []() { + for (const auto& mod : modifier_map) { + if (mod.second.defidx_redirect) { + logging::Info("%d -> %d", mod.first, mod.second.defidx_redirect); + } + } +}); +static CatCommand save("skinchanger_save", "Save", [](const CCommand& args) { + std::string filename = "skinchanger"; + if (args.ArgC() > 1) { + filename = args.Arg(1); + } + Save(filename); +}); +static CatCommand load("skinchanger_load", "Load", [](const CCommand& args) { + std::string filename = "skinchanger"; + if (args.ArgC() > 1) { + filename = args.Arg(1); + } + Load(filename); +}); +static CatCommand remove_redirect("skinchanger_remove_redirect", "Remove redirect", [](const CCommand& args) { + unsigned redirectid = strtoul(args.Arg(1), nullptr, 10); + GetModifier(redirectid).defidx_redirect = 0; + logging::Info("Redirect removed"); + InvalidateCookie(); +}); + +static CatCommand invalidate_cookies("skinchanger_bite_cookie", "Bite Cookie", InvalidateCookie); void FrameStageNotify(int stage) { if (!enabled) return; @@ -105,21 +145,115 @@ void FrameStageNotify(int stage) { int handle = CE_INT(g_pLocalPlayer->entity, netvar.hActiveWeapon); int eid = handle & 0xFFF; IClientEntity* entity = g_IEntityList->GetClientEntity(eid); - GetModifier(NET_INT(entity, netvar.iItemDefinitionIndex)).Apply(eid); - /*if (!GetCookie(weapon->m_IDX).Check()) { - logging::Info("Cookie bad for %i", weapon->m_IDX); // FIXME DEBUG LOGS! - GetModifier(CE_INT(weapon, netvar.iItemDefinitionIndex)).Apply(weapon->m_IDX); - GetCookie(weapon->m_IDX).Update(weapon->m_IDX); - }*/ + if (!entity || entity->IsDormant()) return; + static IClientEntity* last_weapon_out = nullptr; + if ((last_weapon_out != entity) || !cookie.Check()) { + GetModifier(NET_INT(entity, netvar.iItemDefinitionIndex)).Apply(eid); + cookie.Update(eid); + } + last_weapon_out = entity; } +static CatVar show_debug_info(CV_SWITCH, "skinchanger_debug", "1", "Debug Skinchanger"); + void PaintTraverse() { if (!enabled) return; - if (CE_GOOD(LOCAL_W)) + if (!show_debug_info) return; + if (CE_GOOD(LOCAL_W)) { AddSideString(format("dIDX: ", CE_INT(LOCAL_W, netvar.iItemDefinitionIndex))); + CAttributeList* list = (CAttributeList*)((uintptr_t)(RAW_ENT(LOCAL_W)) + netvar.AttributeList); + for (int i = 0; i < list->m_Attributes.Size(); i++) { + AddSideString(format('[', i, "] ", list->m_Attributes[i].defidx, ": ", list->m_Attributes[i].value)); + } + } + // // Debug info? } +#define BINARY_FILE_WRITE(handle, data) handle.write(reinterpret_cast(&data), sizeof(data)) +#define BINARY_FILE_READ(handle, data) handle.read(reinterpret_cast(&data), sizeof(data)) + +void Save(std::string filename) { + uid_t uid = geteuid(); + passwd* pw = getpwuid(uid); + if (!pw) { + logging::Info("Couldn't get username!"); + return; + } + std::string name(pw->pw_name); + DIR* cathook_directory = opendir(strfmt("/home/%s/.cathook", pw->pw_name)); + if (!cathook_directory) { + logging::Info(".cathook directory doesn't exist, creating one!"); + mkdir(strfmt("/home/%s/.cathook", pw->pw_name), S_IRWXU | S_IRWXG); + } else closedir(cathook_directory); + try { + std::ofstream file("/home/" + name + "/.cathook/" + filename, std::ios::out | std::ios::binary); + BINARY_FILE_WRITE(file, SERIALIZE_VERSION); + size_t size = modifier_map.size(); + BINARY_FILE_WRITE(file, size); + for (const auto& item : modifier_map) { + BINARY_FILE_WRITE(file, item.first); + // modifier data isn't a POD (it contains a vector), we can't BINARY_WRITE it completely. + BINARY_FILE_WRITE(file, item.second.defidx_redirect); + const auto& modifiers = item.second.modifiers; + size_t modifier_count = modifiers.size(); + BINARY_FILE_WRITE(file, modifier_count); + // this code is a bit tricky - I'm treating vector as an array + if (modifier_count) { + file.write(reinterpret_cast(modifiers.data()), modifier_count * sizeof(attribute_s)); + } + } + file.close(); + logging::Info("Writing successful"); + } catch (std::exception& e) { + logging::Info("Writing unsuccessful: %s", e.what()); + } +} + +void Load(std::string filename) { + uid_t uid = geteuid(); + passwd* pw = getpwuid(uid); + if (!pw) { + logging::Info("Couldn't get username!"); + return; + } + std::string name(pw->pw_name); + DIR* cathook_directory = opendir(strfmt("/home/%s/.cathook", pw->pw_name)); + if (!cathook_directory) { + logging::Info(".cathook directory doesn't exist, creating one!"); + mkdir(strfmt("/home/%s/.cathook", pw->pw_name), S_IRWXU | S_IRWXG); + } else closedir(cathook_directory); + try { + std::ifstream file("/home/" + name + "/.cathook/" + filename, std::ios::in | std::ios::binary); + unsigned file_serialize = 0; + BINARY_FILE_READ(file, file_serialize); + if (file_serialize != SERIALIZE_VERSION) { + logging::Info("Outdated/corrupted SkinChanger file! Cannot load this."); + file.close(); + return; + } + size_t size = 0; + BINARY_FILE_READ(file, size); + logging::Info("Reading %i entries...", size); + modifier_map.clear(); + for (int i = 0; i < size; i++) { + int defindex; + BINARY_FILE_READ(file, defindex); + size_t count; + def_attribute_modifier modifier; + BINARY_FILE_READ(file, modifier.defidx_redirect); + BINARY_FILE_READ(file, count); + modifier.modifiers.resize(count); + file.read(reinterpret_cast(modifier.modifiers.data()), sizeof(attribute_s) * count); + modifier_map.insert(std::make_pair(defindex, std::move(modifier))); + } + file.close(); + logging::Info("Reading successful! Result: %i entries.", modifier_map.size()); + } catch (std::exception& e) { + logging::Info("Reading unsuccessful: %s", e.what()); + } +} + void def_attribute_modifier::Set(int id, float value) { for (auto& i : modifiers) { if (i.defidx == id) { @@ -127,29 +261,25 @@ void def_attribute_modifier::Set(int id, float value) { return; } } - attribute_s& attr = modifiers.at(first_free_mod); - first_free_mod++; - attr.defidx = id; - attr.value = value; - logging::Info("new attr: %i %.2f %i", attr.defidx, attr.value, first_free_mod); -} - -void InvalidateCookies() { - logging::Info("All cookies invalidated!"); // FIXME DEBUG LOGS! - for (auto& cookie : cookie_map) { - cookie.second.valid = false; + if (modifiers.size() > 13) { + logging::Info("Woah there, that's too many! Remove some."); + return; } + modifiers.push_back(attribute_s { id, value }); + logging::Info("Added new attribute: %i %.2f (%i)", id, value, modifiers.size()); } -patched_weapon_cookie::patched_weapon_cookie(int entity) { - Update(entity); +void InvalidateCookie() { + cookie.valid = false; } +patched_weapon_cookie::patched_weapon_cookie(int entity) {} + void patched_weapon_cookie::Update(int entity) { IClientEntity* ent = g_IEntityList->GetClientEntity(entity); if (!ent || ent->IsDormant()) return; logging::Info("Updating cookie for %i", entity); // FIXME DEBUG LOGS! - CAttributeList* list = NET_VAR(ent, 0x9c0, CAttributeList*); + CAttributeList* list = (CAttributeList*)((uintptr_t)ent + netvar.AttributeList); attrs = list->m_Attributes.Size(); eidx = entity; defidx = NET_INT(ent, netvar.iItemDefinitionIndex); @@ -161,32 +291,40 @@ bool patched_weapon_cookie::Check() { if (!valid) return false; IClientEntity* ent = g_IEntityList->GetClientEntity(eidx); if (!ent || ent->IsDormant()) return false; - CAttributeList* list = NET_VAR(ent, 0x9c0, CAttributeList*); + CAttributeList* list = (CAttributeList*)((uintptr_t)ent + netvar.AttributeList); if (attrs != list->m_Attributes.Size()) return false; if (eclass != ent->GetClientClass()->m_ClassID) return false; if (defidx != NET_INT(ent, netvar.iItemDefinitionIndex)) return false; return true; } +void def_attribute_modifier::Remove(int id) { + auto it = modifiers.begin(); + while (it != modifiers.end()) { + if ((*it).defidx == id) { + it = modifiers.erase(it); + } else { + ++it; + } + } +} + void def_attribute_modifier::Apply(int entity) { IClientEntity* ent = g_IEntityList->GetClientEntity(entity); if (!ent) return; - //logging::Info("Applying modifiers for %i %i %i", entity, NET_INT(ent, netvar.iItemDefinitionIndex), defidx_redirect); if (defidx_redirect && NET_INT(ent, netvar.iItemDefinitionIndex) != defidx_redirect) { NET_INT(ent, netvar.iItemDefinitionIndex) = defidx_redirect; - logging::Info("Updated DefIDX to %i", NET_INT(ent, netvar.iItemDefinitionIndex)); - GetCookie(entity).valid = false; + logging::Info("Redirect -> %i", NET_INT(ent, netvar.iItemDefinitionIndex)); + GetModifier(defidx_redirect).Apply(entity); return; } - CAttributeList* list = NET_VAR(ent, netvar.AttributeList, CAttributeList*); - //::Info("Attribute list: 0x%08x 0x%08x 0x%08x 0x%08x", 0x9c0, ent, list, (uint32_t)list - (uint32_t)ent); - list->m_Attributes.m_Size = list->m_Attributes.Size(); - //logging::Info("Length: %i", list->m_Attributes.m_Size); - //logging::Info("Base: 0x%08x", list->m_Attributes.Base()); + CAttributeList* list = (CAttributeList*)((uintptr_t)ent + netvar.AttributeList); for (const auto& mod : modifiers) { if (mod.defidx) { - //logging::Info("Setting %i to %.2f", mod.defidx, mod.value); // FIXME DEBUG LOGS! - list->SetAttribute(mod.defidx, mod.value); + //if (mod.value) + list->SetAttribute(mod.defidx, mod.value); + //else + // list->RemoveAttribute(mod.defidx); } } } @@ -200,16 +338,19 @@ def_attribute_modifier& GetModifier(int idx) { } } -patched_weapon_cookie& GetCookie(int idx) { +/*patched_weapon_cookie& GetCookie(int idx) { try { return cookie_map.at(idx); } catch (std::out_of_range& oor) { cookie_map.emplace(idx, patched_weapon_cookie{idx}); return cookie_map.at(idx); } -} +}*/ +// A map that maps an Item Definition Index to a modifier std::unordered_map modifier_map {}; -std::unordered_map cookie_map {}; +// A map that maps an Entity Index to a cookie +//std::unordered_map cookie_map {}; +patched_weapon_cookie cookie { 0 }; }}} diff --git a/src/hacks/SkinChanger.hpp b/src/hacks/SkinChanger.hpp index f16d3934..75dfd49d 100644 --- a/src/hacks/SkinChanger.hpp +++ b/src/hacks/SkinChanger.hpp @@ -54,6 +54,7 @@ public: class CAttributeList { public: CAttributeList(); + float GetAttribute(int defindex); void SetAttribute(int index, float value); void RemoveAttribute(int index); public: @@ -66,7 +67,8 @@ enum class Attributes { is_australium_item = 2027, item_style_override = 542, sheen = 2014, - killstreak_tier = 2025 + killstreak_tier = 2025, + set_item_texture_wear = 725 }; enum class UnusualEffects { @@ -76,6 +78,16 @@ enum class UnusualEffects { ENERGY_ORB }; +enum class Sheens { + TEAM_SHINE = 1, + DEADLY_DAFFODIL, + MANNDARIN, + MEAN_GREEN, + AGONIZING_EMERALD, + VILLAINOUS_VIOLET, + HOT_ROD +}; + struct patched_weapon_cookie { patched_weapon_cookie(int entity); void Update(int entity); @@ -91,19 +103,24 @@ public: struct def_attribute_modifier { void Apply(int entity); void Set(int id, float value); + void Remove(int id); int defidx { 0 }; int defidx_redirect { 0 }; - std::array modifiers { attribute_s{ 0, 0 } }; - int first_free_mod { 0 }; + std::vector modifiers { }; }; extern std::unordered_map modifier_map; -extern std::unordered_map cookie_map; +extern patched_weapon_cookie cookie; +//extern std::unordered_map cookie_map; def_attribute_modifier& GetModifier(int idx); -patched_weapon_cookie& GetCookie(int idx); +//patched_weapon_cookie& GetCookie(int idx); -void InvalidateCookies(); +constexpr unsigned SERIALIZE_VERSION = 1; +void Save(std::string filename); +void Load(std::string filename); + +void InvalidateCookie(); void FrameStageNotify(int stage); void PaintTraverse(); diff --git a/src/netvars.cpp b/src/netvars.cpp index 43abb476..e8294c58 100644 --- a/src/netvars.cpp +++ b/src/netvars.cpp @@ -30,7 +30,7 @@ void NetVars::Init() { //this->flReloadPriorNextFire = gNetvars.get_offset("DT_TFWeaponBase", "LocalActiveTFWeaponData", "m_flReloadPriorNextFire"); //this->flObservedCritChance = gNetvars.get_offset("DT_TFWeaponBase", "LocalActiveTFWeaponData", "m_flObservedCritChance"); this->iItemDefinitionIndex = gNetvars.get_offset("DT_EconEntity", "m_AttributeManager", "m_Item", "m_iItemDefinitionIndex"); - this->AttributeList = gNetvars.get_offset("DT_EconEntity", "m_AttributeManager", "m_Item", "m_AttributeList") + 8; // hmmm + this->AttributeList = gNetvars.get_offset("DT_EconEntity", "m_AttributeManager", "m_Item", "m_AttributeList"); // hmmm this->flChargeBeginTime = gNetvars.get_offset("DT_WeaponPipebombLauncher", "PipebombLauncherLocalData", "m_flChargeBeginTime"); this->flLastFireTime = gNetvars.get_offset("DT_TFWeaponBase", "LocalActiveTFWeaponData", "m_flLastFireTime"); this->bDistributed = gNetvars.get_offset("DT_CurrencyPack", "m_bDistributed");