Here is a collection of classes you can use to get CVars and modify them with zero function calls and zero client hooks. Very easy to modify to work from outside WoW in an external app.
Uses include:
Speedhacks in WoW | Ramblings++
Forcing Cvars beyond their allowed ranges (target distance, camera angles, camera zoom, etc). Forcing CVars to values that are typically restricted. Etc.
Again. Huge huge credits to Greyman for his help with the hashbucket enumeration.
Credits to Bsing for CFindPattern and CLog.
Cvar.h:
Cvar.cppCode:#pragma once // C++ Standard Library #include <map> #include <string> // Forward declarations class WoWCVar; class WoWCvarArray; // Cvar container for GetCvars typedef std::map<std::string,WoWCVar*> WoWCvars; // WoW's hashbucket class template <class T> class WoWTSHashObject { public: // Gets the object hash unsigned int GetHash() { return m_Hash; } // Gets the next object in the list T* GetNext() { return m_Next; } // Gets the next object in the sub-list (objects with the same hash) T* GetNextSub(unsigned int SubStep) { return (*(T**)((unsigned int)(this) + SubStep + 4)); } private: unsigned int m_Hash; // 0x00 -> 0x04 unsigned char Unk04[0x0C]; // 0x04 -> 0x10 T* m_Next; // 0x10 -> 0x14 }; // sizeof(TSObjectArray) = 0x14 // WoW's Cvar class. Class is implemented as a hash bucket through inheritance // of WoWTSHashObject. class WoWCVar : public WoWTSHashObject<WoWCVar> { public: // Get name of cvar const char* GetName() { return m_Name; } // Get category of cvar int GetCategory() { return m_Category; } // Get flags of cvar int GetFlags() { return m_Flags; } // Get value of cvar as string const char* GetStringVal() { return m_StringVal; } // Get value of cvar as float float GetFloatVal() { return m_FloatVal; } // Get value of cvar as int int GetIntVal() { return m_IntVal; } // Get the number of times the cvar has been modified (by 'legit' method) int GetNumMods() { return m_NumMods; } // Get the default value of the cvar const char* GetDefaultVal() { return m_DefaultVal; } private: char* m_Name; // 0x14 -> 0x18 int m_Category; // 0x18 -> 0x1C int m_Flags; // 0x1C -> 0x20 int m_Unk20; // 0x20 -> 0x24 int m_Unk24; // 0x24 -> 0x28 char* m_StringVal; // 0x28 -> 0x2C float m_FloatVal; // 0x2C -> 0x30 int m_IntVal; // 0x30 -> 0x34 int m_NumMods; // 0x34 -> 0x38 int m_Unk38; // 0x38 -> 0x3C int m_Unk3C; // 0x3C -> 0x40 char* m_DefaultVal; // 0x40 -> 0x44 }; // sizeof(WoWCVar) = ? // Class that (most importantly) holds the array base for WoWCvars class WoWCvarBaseMgr { public: static WoWCvars GetCvars(); static WoWCVar* GetCvar(const std::string& Name); WoWCvarArray* GetArrayBase() { return m_pArrayBase; } private: unsigned char Unk00[0x1C]; WoWCvarArray* m_pArrayBase; }; // sizeof(WoWCvarBaseMgr) = ? // WoWCvar array class. Holds an array of WoWTSHashObjects class WoWCvarArray { public: WoWCVar* GetCvar(unsigned int Index) { return *(WoWCVar**)((unsigned int)(this) + Index * 12 + 8); } unsigned int GetSubStep(unsigned int Index) { return *(unsigned int*)((unsigned int)(this) + Index * 12); } }; // sizeof(WoWCVarArray) = ?
Pattern:Code:// Red Pill #include "Cvar.h" #include "RedPillMgr.h" #include "FindPattern.h" // Gets all of the cvars currently loaded WoWCvars WoWCvarBaseMgr::GetCvars() { // Container to hold cvars (return value) WoWCvars Cvars; // Get CVar base manager WoWCvarBaseMgr* pCvarBaseMgr = reinterpret_cast<WoWCvarBaseMgr*>(RedPillMgr::Get()->GetFindPattern()->GetAddress("WoWCvarBaseMgr")); // Get Cvar array pointer WoWCvarArray* pCvarArray = pCvarBaseMgr->GetArrayBase(); // Array size // TODO: Track down array size in WoWCvarBaseMgr unsigned int ArraySize = 0x100; // Enumerate cvar array for (unsigned int i = 0; i < ArraySize; i++) { // Get Cvar for index WoWCVar* pCvar = pCvarArray->GetCvar(i); // Get substep for index unsigned int SubStep = pCvarArray->GetSubStep(i); // Unknown number of substeps. Loop till break. for (;;) { // Check Cvar pointer is valid if (pCvar && (reinterpret_cast<unsigned int>(pCvar) & 1) == 0) { // Check Cvar hash is valid if (pCvar->GetHash()) { // Store cvar Cvars[pCvar->GetName()] = pCvar; // Get next sub-cvar pCvar = pCvar->GetNextSub(SubStep); // Loop again continue; } } // No more valid sub-cvars break; } } // Return Cvar container return Cvars; } // Gets a cvar by name WoWCVar* WoWCvarBaseMgr::GetCvar(const std::string& Name) { // Return value. Pointer to cvar WoWCVar* pReturn = 0; // Get CVar base manager WoWCvarBaseMgr* pCvarBaseMgr = reinterpret_cast<WoWCvarBaseMgr*>(RedPillMgr::Get()->GetFindPattern()->GetAddress("WoWCvarBaseMgr")); // Get Cvar array pointer WoWCvarArray* pCvarArray = pCvarBaseMgr->GetArrayBase(); // Array size // TODO: Track down array size in WoWCvarBaseMgr unsigned int ArraySize = 0x100; // Enumerate cvar array for (unsigned int i = 0; i < ArraySize; i++) { // Get Cvar for index WoWCVar* pCvar = pCvarArray->GetCvar(i); // Get substep for index unsigned int SubStep = pCvarArray->GetSubStep(i); // Unknown number of substeps. Loop till break. for (;;) { // Check Cvar pointer is valid if (pCvar && (reinterpret_cast<unsigned int>(pCvar) & 1) == 0) { // Check Cvar hash is valid if (pCvar->GetHash()) { // Return the CVar if it's the requested one if (pCvar->GetName() == Name) return pCvar; // Get next sub-cvar pCvar = pCvar->GetNextSub(SubStep); // Loop again continue; } } // No more valid sub-cvars break; } } // Return null pointer. Cvar not found return pReturn; }
Address:Code:<Pattern desc="WoWCvarBaseMgr" pattern="\x53\x56\x57\x68\x00\x00\x00\x00\xE8\x00\x00\x00\x00\x8B\x70" mask="xxxx????x????xx"> <Add value="9"/> <Rel offset="1" size="5"/> <Add value="C"/> <Lea/> </Pattern>
Example Usage:Code:[10:11:25]: [FindPattern]: 0x011FCC30 -> WoWCvarBaseMgr
Screenshot:Code:// Callback for the 'dumpcvars' command void CEGUIConsole::DumpCvarsCallback(const std::string& /*Params*/) { // Get all loaded cvars WoWCvars MyCvars = WoWCvarBaseMgr::GetCvars(); // Iterate through all cvars and dump out debug info for each(std::pair<std::string,WoWCVar*> MyPair in MyCvars) { WoWCVar* pCvar = MyPair.second; DBGLOG(boost::str(boost::format("[DumpCvars]: Name: %s") %pCvar->GetName())); DBGLOG(boost::str(boost::format("[DumpCvars]: Category: %u") %pCvar->GetCategory())); DBGLOG(boost::str(boost::format("[DumpCvars]: Flags: %08X") %pCvar->GetFlags())); DBGLOG(boost::str(boost::format("[DumpCvars]: StringVal: %s") %pCvar->GetStringVal())); DBGLOG(boost::str(boost::format("[DumpCvars]: FloatVal: %f") %pCvar->GetFloatVal())); DBGLOG(boost::str(boost::format("[DumpCvars]: IntVal: %d") %pCvar->GetIntVal())); DBGLOG(boost::str(boost::format("[DumpCvars]: NumMods: %u") %pCvar->GetNumMods())); std::string DefaultVal = pCvar->GetDefaultVal() ? pCvar->GetDefaultVal() : "<none>"; DBGLOG(boost::str(boost::format("[DumpCvars]: DefaultVal: %s") %DefaultVal)); } // Append the text to be echoed WriteOutput(boost::str(boost::format("[DumpCvars]: Dumped %u cvars.") %MyCvars.size())); } // Callback for the 'cvar' command void CEGUIConsole::CvarCallback(const std::string& Params) { // Get all loaded cvars WoWCvars MyCvars = WoWCvarBaseMgr::GetCvars(); // Find cvar by name WoWCvars::iterator iCvar = MyCvars.find(Params); // Check if valid cvar was found if (iCvar == MyCvars.end()) { WriteOutput(boost::str(boost::format("[Cvar]: Error. Could not find cvar with name %s.") %Params)); return; } // Dump info for cvar to console WoWCVar* pCvar = iCvar->second; WriteOutput(boost::str(boost::format("[Cvar]: Name: %s") %pCvar->GetName())); WriteOutput(boost::str(boost::format("[Cvar]: Category: %u") %pCvar->GetCategory())); WriteOutput(boost::str(boost::format("[Cvar]: Flags: %08X") %pCvar->GetFlags())); WriteOutput(boost::str(boost::format("[Cvar]: StringVal: %s") %pCvar->GetStringVal())); WriteOutput(boost::str(boost::format("[Cvar]: FloatVal: %f") %pCvar->GetFloatVal())); WriteOutput(boost::str(boost::format("[Cvar]: IntVal: %d") %pCvar->GetIntVal())); WriteOutput(boost::str(boost::format("[Cvar]: NumMods: %u") %pCvar->GetNumMods())); std::string DefaultVal = pCvar->GetDefaultVal() ? pCvar->GetDefaultVal() : "<none>"; WriteOutput(boost::str(boost::format("[Cvar]: DefaultVal: %s") %DefaultVal)); }
Notes:
a) In the example usage I don't use the singular GetCvar because I added that after writing the example.
b) The example uses the 'for each' MSVC++ language extension. You will need to modify it if you use Intel/GCC/etc or want portable code.
c) I probably missed or overlooked something but whatever. Have fun!
If anyone reverses more of the cvar structure or the flags please post here because I'm lazy.