topraksama
Master Üye
- Katılım
- 6 Şub 2026
- Mesajlar
- 1,252
- Beğeniler
- 205
Kod:
#include "pch.h"
#include "skinchanger.h"
#include "config.h"
#include "offsets.h"
#include <Psapi.h>
#include <map>
#include <mutex>
#include <atomic>
#include <vector>
#pragma comment(lib, "psapi.lib")
namespace SkinChanger {
// --- Attribute struct matching game layout ---
#pragma pack(push, 1)
struct CEconItemAttribute {
uintptr_t vtable;
uintptr_t owner;
char pad[32];
uint16_t defIndex;
char pad2[2];
float value;
float initValue;
int32_t refundableCurrency;
bool setBonus;
char pad3[7];
}; // 0x48
struct CPtrGameVector {
uint64_t size;
uintptr_t ptr;
};
#pragma pack(pop)
// --- State ---
static CEconItemAttribute* g_attrBuffer = nullptr;
static uintptr_t regenAddr = 0;
static bool regenPatched = false;
static uintptr_t lastAppliedWeapon = 0;
static int lastAppliedKit = 0;
static uint16_t lastOriginalDefIndex = 0;
// --- Helpers ---
static CEconItemAttribute MakeAttribute(uint16_t def, float val) {
CEconItemAttribute attr{};
attr.defIndex = def;
attr.value = val;
attr.initValue = val;
return attr;
}
static uintptr_t GetEntityByHandle(uintptr_t entityList, uint32_t handle) {
if (!handle || handle == 0xFFFFFFFF) return 0;
uint32_t idx = handle & 0x7FFF;
uintptr_t listEntry = *(uintptr_t*)(entityList + 0x10 + 8 * (idx >> 9));
if (!listEntry) return 0;
return *(uintptr_t*)(listEntry + 0x70 * (idx & 0x1FF));
}
// --- Sig scan ---
static uintptr_t SigScan(uintptr_t base, size_t size, const char* pattern) {
auto parse = [](const char* pat) {
std::vector<std::pair<uint8_t, bool>> bytes;
const char* s = pat;
const char* e = s + strlen(pat);
while (s < e) {
if (*s == '?') { s++; if (*s == '?') s++; bytes.emplace_back(0, true); }
else { bytes.emplace_back((uint8_t)strtoul(s, const_cast<char**>(&s), 16), false); }
while (*s == ' ') s++;
}
return bytes;
};
auto sig = parse(pattern);
uint8_t* scan = (uint8_t*)base;
for (size_t i = 0; i < size - sig.size(); i++) {
bool found = true;
for (size_t j = 0; j < sig.size(); j++) {
if (!sig[j].second && scan[i + j] != sig[j].first) { found = false; break; }
}
if (found) return base + i;
}
return 0;
}
static void InitRegen() {
if (regenAddr) return;
HMODULE clientMod = GetModuleHandleA("client.dll");
if (!clientMod) return;
MODULEINFO mi{};
if (!GetModuleInformation(GetCurrentProcess(), clientMod, &mi, sizeof(mi))) return;
regenAddr = SigScan((uintptr_t)clientMod, mi.SizeOfImage,
"48 83 EC ? E8 ? ? ? ? 48 85 C0 0F 84 ? ? ? ? 48 8B 10");
if (!regenAddr) return;
uint16_t combined = (uint16_t)(schema::m_AttributeManager + schema::m_Item +
schema::m_AttributeList + schema::m_Attributes);
DWORD oldProt;
if (VirtualProtect((void*)(regenAddr + 0x52), 2, PAGE_EXECUTE_READWRITE, &oldProt)) {
*(uint16_t*)(regenAddr + 0x52) = combined;
VirtualProtect((void*)(regenAddr + 0x52), 2, oldProt, &oldProt);
regenPatched = true;
}
}
static void CallRegen() {
if (!regenAddr || !regenPatched) return;
__try {
typedef void(__fastcall* RegenFn)();
((RegenFn)regenAddr)();
} __except (EXCEPTION_EXECUTE_HANDLER) {}
}
static void CreateAttributes(uintptr_t item, int paintKit, int seed, float wear) {
if (paintKit <= 0) return;
uintptr_t addr = item + schema::m_AttributeList + schema::m_Attributes;
CPtrGameVector existing = *(CPtrGameVector*)addr;
if (existing.size > 0 || existing.ptr != 0) return;
if (!g_attrBuffer) {
g_attrBuffer = (CEconItemAttribute*)VirtualAlloc(nullptr,
sizeof(CEconItemAttribute) * 3, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!g_attrBuffer) return;
}
g_attrBuffer[0] = MakeAttribute(6, (float)paintKit);
g_attrBuffer[1] = MakeAttribute(7, (float)seed);
g_attrBuffer[2] = MakeAttribute(8, wear);
CPtrGameVector vec;
vec.size = 3;
vec.ptr = (uintptr_t)g_attrBuffer;
*(CPtrGameVector*)addr = vec;
}
static void RemoveAttributes(uintptr_t item) {
uintptr_t addr = item + schema::m_AttributeList + schema::m_Attributes;
CPtrGameVector existing = *(CPtrGameVector*)addr;
if (existing.size == 0) return;
CPtrGameVector empty{};
*(CPtrGameVector*)addr = empty;
}
static void ApplyAndRegen(uintptr_t weapon, int paintKit, float wear, int seed, int statTrak) {
uintptr_t item = weapon + schema::m_AttributeManager + schema::m_Item;
// Mark as custom item so fallback values are used
*(uint32_t*)(item + schema::m_iItemIDHigh) = 0;
*(uint32_t*)(item + schema::m_iItemIDLow) = 1;
// Set fallback values - intentionally NOT reset afterwards so the skin persists
*(int32_t*)(weapon + schema::m_nFallbackPaintKit) = paintKit;
*(float*) (weapon + schema::m_flFallbackWear) = wear;
*(int32_t*)(weapon + schema::m_nFallbackSeed) = seed;
*(int32_t*)(weapon + schema::m_nFallbackStatTrak) = statTrak;
CreateAttributes(item, paintKit, seed, wear);
CallRegen();
RemoveAttributes(item);
}
// --- SetModel hook for knife model redirect ---
using SetModelFn = void(__fastcall*)(void*, const char*);
static SetModelFn g_OriginalSetModel = nullptr;
static bool IsDefaultKnifeModel(const char* model) {
return model && (
strstr(model, "v_knife_default") != nullptr ||
strstr(model, "v_knife_t.vmdl") != nullptr
);
}
static const char* GetKnifeModel(int defIndex) {
switch (defIndex) {
case 500: return "models/weapons/v_knife_bayonet.vmdl";
case 505: return "models/weapons/v_knife_flip.vmdl";
case 506: return "models/weapons/v_knife_gut.vmdl";
case 507: return "models/weapons/v_knife_karam.vmdl";
case 508: return "models/weapons/v_knife_m9_bay.vmdl";
case 509: return "models/weapons/v_knife_tactical.vmdl";
case 512: return "models/weapons/v_knife_falchion_advanced.vmdl";
case 514: return "models/weapons/v_knife_survival_bowie.vmdl";
case 515: return "models/weapons/v_knife_butterfly.vmdl";
case 516: return "models/weapons/v_knife_push.vmdl";
case 517: return "models/weapons/v_knife_cord.vmdl";
case 519: return "models/weapons/v_knife_ursus.vmdl";
case 520: return "models/weapons/v_knife_gypsy_jackknife.vmdl";
case 521: return "models/weapons/v_knife_outdoor.vmdl";
case 522: return "models/weapons/v_knife_stiletto.vmdl";
case 523: return "models/weapons/v_knife_widowmaker.vmdl";
case 525: return "models/weapons/v_knife_skeleton.vmdl";
case 526: return "models/weapons/v_knife_kukri.vmdl";
default: return nullptr;
}
}
static void __fastcall HookedSetModel(void* entity, const char* model) {
if (g_Config.skinChangerEnabled && model && IsDefaultKnifeModel(model)) {
auto it = g_Config.weaponSkins.find(g_Config.knifeDefIndex);
if (it != g_Config.weaponSkins.end() && it->second.enabled) {
const char* knifeModel = GetKnifeModel(g_Config.knifeDefIndex);
if (knifeModel) {
g_OriginalSetModel(entity, knifeModel);
return;
}
}
}
g_OriginalSetModel(entity, model);
}
void Init() {
HMODULE clientMod = GetModuleHandleA("client.dll");
if (!clientMod) return;
MODULEINFO mi{};
if (!GetModuleInformation(GetCurrentProcess(), clientMod, &mi, sizeof(mi))) return;
uintptr_t setModelAddr = SigScan((uintptr_t)clientMod, mi.SizeOfImage,
"40 53 48 83 EC 20 48 8B D9 4C 8B C2 48 8B 0D ? ? ? ? 48 8D 54 24");
if (!setModelAddr) return;
if (MH_CreateHook((void*)setModelAddr, &HookedSetModel, (void**)&g_OriginalSetModel) == MH_OK)
MH_EnableHook((void*)setModelAddr);
}
void Tick() {
if (!g_Config.skinChangerEnabled) return;
__try {
uintptr_t clientBase = (uintptr_t)GetModuleHandleA("client.dll");
if (!clientBase) return;
uintptr_t localPawn = *(uintptr_t*)(clientBase + offsets::dwLocalPlayerPawn);
if (!localPawn) { lastAppliedWeapon = 0; lastAppliedKit = 0; return; }
uint8_t lifeState = *(uint8_t*)(localPawn + schema::m_lifeState);
int health = *(int*)(localPawn + schema::m_iHealth);
if (lifeState != 0 || health <= 0) { lastAppliedWeapon = 0; lastAppliedKit = 0; return; }
InitRegen();
uintptr_t activeWeapon = *(uintptr_t*)(localPawn + schema::m_pClippingWeapon);
if (!activeWeapon) return;
uintptr_t item = activeWeapon + schema::m_AttributeManager + schema::m_Item;
uint16_t defIndex = *(uint16_t*)(item + schema::m_iItemDefinitionIndex);
bool isWeapon = (defIndex > 0 && defIndex < 70) || (defIndex >= 500 && defIndex < 600);
if (!isWeapon || defIndex == 31) return; // skip C4
int lookupIndex = defIndex;
// Map default knives to the configured knife type
if (defIndex == 42 || defIndex == 59)
lookupIndex = g_Config.knifeDefIndex;
auto it = g_Config.weaponSkins.find(lookupIndex);
if (it == g_Config.weaponSkins.end() || !it->second.enabled || it->second.paintKit <= 0)
return;
auto& skin = it->second;
bool needsApply = g_Config.skinForceUpdate
|| (activeWeapon != lastAppliedWeapon)
|| (skin.paintKit != lastAppliedKit);
if (!needsApply) return;
// Double check alive
lifeState = *(uint8_t*)(localPawn + schema::m_lifeState);
health = *(int*)(localPawn + schema::m_iHealth);
if (lifeState != 0 || health <= 0) return;
// For knives: overwrite the item definition index to change the skin lookup
bool isKnife = (defIndex == 42 || defIndex == 59);
if (isKnife && lookupIndex != (int)defIndex)
*(uint16_t*)(item + schema::m_iItemDefinitionIndex) = (uint16_t)lookupIndex;
ApplyAndRegen(activeWeapon, skin.paintKit, skin.wear, skin.seed, skin.statTrak);
lastAppliedWeapon = activeWeapon;
lastAppliedKit = skin.paintKit;
g_Config.skinForceUpdate = false;
} __except (EXCEPTION_EXECUTE_HANDLER) {
lastAppliedWeapon = 0;
lastAppliedKit = 0;
}
}
void Shutdown() {
// Restore original knife defIndex if patched
if (lastOriginalDefIndex && lastAppliedWeapon) {
__try {
uintptr_t clientBase = (uintptr_t)GetModuleHandleA("client.dll");
if (clientBase) {
uintptr_t localPawn = *(uintptr_t*)(clientBase + offsets::dwLocalPlayerPawn);
if (localPawn) {
uintptr_t item = lastAppliedWeapon + schema::m_AttributeManager + schema::m_Item;
*(uint16_t*)(item + schema::m_iItemDefinitionIndex) = lastOriginalDefIndex;
}
}
} __except (EXCEPTION_EXECUTE_HANDLER) {}
}
if (g_attrBuffer) {
VirtualFree(g_attrBuffer, 0, MEM_RELEASE);
g_attrBuffer = nullptr;
}
}
}
#pragma once
namespace SkinChanger {
void Init();
void Tick();
void Shutdown();
}
Üstün hesaplamalarıma göre güncel olarak çalışıyor. Deniyen olursa konuya görselli şekilde atarsa sevinirim.