This commit is contained in:
Oscar
2025-07-29 20:23:06 +03:00
parent 860be9ac4c
commit 5717c7999c
253 changed files with 873847 additions and 0 deletions

View File

@@ -0,0 +1,406 @@
// used: [win] winapi
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include "config.h"
// used: getworkingpath
#include "../core.h"
// used: l_print
#include "../utilities/log.h"
// used: integertostring
#include "../utilities/crt.h"
// used: heapalloc, heapfree
#include "../utilities/memory.h"
// used: formatter implementation
#if defined(CS_CONFIGURATION_BINARY)
#include "../../extensions/binary.h"
#elif defined(CS_CONFIGURATION_JSON)
#include "../../extensions/json.h"
#elif defined(CS_CONFIGURATION_TOML)
#include "../../extensions/toml.h"
#endif
// default configurations working path
static wchar_t wszConfigurationsPath[MAX_PATH];
#pragma region config_user_data_type
std::size_t C::UserDataType_t::GetSerializationSize() const
{
std::size_t nTotalDataSize = 0U;
for (const UserDataMember_t& member : vecMembers)
nTotalDataSize += sizeof(FNV1A_t[2]) + member.nDataSize;
return nTotalDataSize;
}
#pragma endregion
#pragma region config_variable_object
void C::VariableObject_t::SetStorage(const void* pValue)
{
// check is available to store value in the local storage
if (this->nStorageSize <= sizeof(this->storage.uLocal))
{
CRT::MemorySet(&this->storage.uLocal, 0U, sizeof(this->storage.uLocal));
CRT::MemoryCopy(&this->storage.uLocal, pValue, this->nStorageSize);
}
// otherwise use heap memory to store it
else
{
CS_ASSERT(this->storage.pHeap != nullptr); // tried to access non allocated storage
CRT::MemorySet(this->storage.pHeap, 0U, this->nStorageSize);
CRT::MemoryCopy(this->storage.pHeap, pValue, this->nStorageSize);
}
}
std::size_t C::VariableObject_t::GetSerializationSize() const
{
std::size_t nSerializationSize = this->nStorageSize;
// denote a custom serialization size when it different from the storage size
switch (this->uTypeHash)
{
// lookup for array data type
case FNV1A::HashConst("bool[]"):
case FNV1A::HashConst("int[]"):
case FNV1A::HashConst("unsigned int[]"):
case FNV1A::HashConst("float[]"):
case FNV1A::HashConst("char[][]"):
// arrays also serialize their size
nSerializationSize += sizeof(std::size_t);
break;
// lookup for user-defined data type
default:
{
for (const UserDataType_t& userType : vecUserTypes)
{
if (userType.uTypeHash == this->uTypeHash)
{
nSerializationSize = sizeof(std::size_t) + userType.GetSerializationSize();
break;
}
}
break;
}
}
return nSerializationSize;
}
#pragma endregion
bool C::Setup(const wchar_t* wszDefaultFileName)
{
if (!CORE::GetWorkingPath(wszConfigurationsPath))
return false;
CRT::StringCat(wszConfigurationsPath, CS_XOR(L"settings\\"));
// create directory if it doesn't exist
if (!::CreateDirectoryW(wszConfigurationsPath, nullptr))
{
if (::GetLastError() != ERROR_ALREADY_EXISTS)
{
L_PRINT(LOG_ERROR) << CS_XOR("failed to create configurations directory, because one or more intermediate directories don't exist");
return false;
}
}
// @note: define custom data types we want to serialize
AddUserType(FNV1A::HashConst("KeyBind_t"),
{
UserDataMember_t{ FNV1A::HashConst("uKey"), FNV1A::HashConst("unsigned int"), &KeyBind_t::uKey },
UserDataMember_t{ FNV1A::HashConst("nMode"), FNV1A::HashConst("int"), &KeyBind_t::nMode }
});
AddUserType(FNV1A::HashConst("ColorPickerVar_t"),
{
UserDataMember_t{ FNV1A::HashConst("bRainbow"), FNV1A::HashConst("bool"), &ColorPickerVar_t::bRainbow },
UserDataMember_t{ FNV1A::HashConst("flRainbowSpeed"), FNV1A::HashConst("float"), &ColorPickerVar_t::flRainbowSpeed },
UserDataMember_t{ FNV1A::HashConst("colPrimary"), FNV1A::HashConst("Color_t"), &ColorPickerVar_t::colValue },
});
AddUserType(FNV1A::HashConst("TextOverlayVar_t"),
{
UserDataMember_t{ FNV1A::HashConst("bEnable"), FNV1A::HashConst("bool"), &TextOverlayVar_t::bEnable },
UserDataMember_t{ FNV1A::HashConst("flThickness"), FNV1A::HashConst("float"), &TextOverlayVar_t::flThickness },
UserDataMember_t{ FNV1A::HashConst("colPrimary"), FNV1A::HashConst("Color_t"), &TextOverlayVar_t::colPrimary },
UserDataMember_t{ FNV1A::HashConst("colOutline"), FNV1A::HashConst("Color_t"), &TextOverlayVar_t::colOutline }
});
AddUserType(FNV1A::HashConst("FrameOverlayVar_t"),
{
UserDataMember_t{ FNV1A::HashConst("bEnable"), FNV1A::HashConst("bool"), &FrameOverlayVar_t::bEnable },
UserDataMember_t{ FNV1A::HashConst("flThickness"), FNV1A::HashConst("float"), &FrameOverlayVar_t::flThickness },
UserDataMember_t{ FNV1A::HashConst("flRounding"), FNV1A::HashConst("float"), &FrameOverlayVar_t::flRounding },
UserDataMember_t{ FNV1A::HashConst("colPrimary"), FNV1A::HashConst("Color_t"), &FrameOverlayVar_t::colPrimary },
UserDataMember_t{ FNV1A::HashConst("colOutline"), FNV1A::HashConst("Color_t"), &FrameOverlayVar_t::colOutline }
});
AddUserType(FNV1A::HashConst("BarOverlayVar_t"),
{
UserDataMember_t{ FNV1A::HashConst("bEnable"), FNV1A::HashConst("bool"), &BarOverlayVar_t::bEnable },
UserDataMember_t{ FNV1A::HashConst("bGradient"), FNV1A::HashConst("bool"), &BarOverlayVar_t::bGradient },
UserDataMember_t{ FNV1A::HashConst("bUseFactorColor"), FNV1A::HashConst("bool"), &BarOverlayVar_t::bUseFactorColor },
UserDataMember_t{ FNV1A::HashConst("flThickness"), FNV1A::HashConst("float"), &BarOverlayVar_t::flThickness },
UserDataMember_t{ FNV1A::HashConst("colPrimary"), FNV1A::HashConst("Color_t"), &BarOverlayVar_t::colPrimary },
UserDataMember_t{ FNV1A::HashConst("colSecondary"), FNV1A::HashConst("Color_t"), &BarOverlayVar_t::colSecondary },
UserDataMember_t{ FNV1A::HashConst("colBackground"), FNV1A::HashConst("Color_t"), &BarOverlayVar_t::colBackground },
UserDataMember_t{ FNV1A::HashConst("colOutline"), FNV1A::HashConst("Color_t"), &BarOverlayVar_t::colOutline }
});
// create default configuration
if (!CreateFile(wszDefaultFileName))
return false;
// store existing configurations list
Refresh();
return true;
}
#pragma region config_main
void C::Refresh()
{
// clear and free previous stored file names
vecFileNames.clear();
// make configuration files path filter
wchar_t wszPathFilter[MAX_PATH];
CRT::StringCat(CRT::StringCopy(wszPathFilter, wszConfigurationsPath), CS_XOR(L"*" CS_CONFIGURATION_FILE_EXTENSION));
// iterate through all files with our filter
WIN32_FIND_DATAW findData;
if (const HANDLE hFindFile = ::FindFirstFileW(wszPathFilter, &findData); hFindFile != INVALID_HANDLE_VALUE)
{
do
{
vecFileNames.push_back(new wchar_t[CRT::StringLength(findData.cFileName) + 1U]);
CRT::StringCopy(vecFileNames.back(), findData.cFileName);
L_PRINT(LOG_INFO) << CS_XOR("found configuration file: \"") << findData.cFileName << CS_XOR("\"");
} while (::FindNextFileW(hFindFile, &findData));
::FindClose(hFindFile);
}
}
void C::AddUserType(const FNV1A_t uTypeHash, const std::initializer_list<UserDataMember_t> vecUserMembers)
{
if (vecUserMembers.size() == 0U)
return;
UserDataType_t userDataType;
userDataType.uTypeHash = uTypeHash;
for (const auto& userDataMember : vecUserMembers)
userDataType.vecMembers.push_back(userDataMember);
vecUserTypes.emplace_back(CRT::Move(userDataType));
}
bool C::SaveFileVariable(const std::size_t nFileIndex, const VariableObject_t& variable)
{
const wchar_t* wszFileName = vecFileNames[nFileIndex];
wchar_t wszFilePath[MAX_PATH];
CRT::StringCat(CRT::StringCopy(wszFilePath, wszConfigurationsPath), wszFileName);
#if defined(CS_CONFIGURATION_BINARY)
if (BIN::SaveVariable(wszFilePath, variable))
#elif defined(CS_CONFIGURATION_JSON)
if (JSON::SaveVariable(wszFilePath, variable))
#elif defined(CS_CONFIGURATION_TOML)
if (TOML::SaveVariable(wszFilePath, variable))
#endif
{
return true;
}
return false;
}
bool C::LoadFileVariable(const std::size_t nFileIndex, VariableObject_t& variable)
{
const wchar_t* wszFileName = vecFileNames[nFileIndex];
wchar_t wszFilePath[MAX_PATH];
CRT::StringCat(CRT::StringCopy(wszFilePath, wszConfigurationsPath), wszFileName);
#if defined(CS_CONFIGURATION_BINARY)
if (BIN::LoadVariable(wszFilePath, variable))
#elif defined(CS_CONFIGURATION_JSON)
if (JSON::LoadVariable(wszFilePath, variable))
#elif defined(CS_CONFIGURATION_TOML)
if (TOML::LoadVariable(wszFilePath, variable))
#endif
{
return true;
}
return false;
}
bool C::RemoveFileVariable(const std::size_t nFileIndex, const VariableObject_t& variable)
{
const wchar_t* wszFileName = vecFileNames[nFileIndex];
wchar_t wszFilePath[MAX_PATH];
CRT::StringCat(CRT::StringCopy(wszFilePath, wszConfigurationsPath), wszFileName);
#if defined(CS_CONFIGURATION_BINARY)
if (BIN::RemoveVariable(wszFilePath, variable))
#elif defined(CS_CONFIGURATION_JSON)
if (JSON::RemoveVariable(wszFilePath, variable))
#elif defined(CS_CONFIGURATION_TOML)
if (TOML::RemoveVariable(wszFilePath, variable))
#endif
{
return true;
}
return false;
}
bool C::CreateFile(const wchar_t* wszFileName)
{
const wchar_t* wszFileExtension = CRT::StringCharR(wszFileName, L'.');
// get length of the given filename and strip out extension if there any
const std::size_t nFileNameLength = (wszFileExtension != nullptr ? wszFileExtension - wszFileName : CRT::StringLength(wszFileName));
wchar_t* wszFullFileName = new wchar_t[nFileNameLength + CRT::StringLength(CS_CONFIGURATION_FILE_EXTENSION) + 1U];
// copy filename without extension
wchar_t* wszFullFileNameEnd = CRT::StringCopyN(wszFullFileName, wszFileName, nFileNameLength);
*wszFullFileNameEnd = L'\0';
// append correct extension to the filename
CRT::StringCat(wszFullFileNameEnd, CS_XOR(CS_CONFIGURATION_FILE_EXTENSION));
// add filename to the list
vecFileNames.push_back(wszFullFileName);
// create and save it by the index
if (SaveFile(vecFileNames.size() - 1U))
{
L_PRINT(LOG_INFO) << CS_XOR("created configuration file: \"") << wszFullFileName << CS_XOR("\"");
return true;
}
L_PRINT(LOG_WARNING) << CS_XOR("failed to create configuration file: \"") << wszFullFileName << CS_XOR("\"");
return false;
}
bool C::SaveFile(const std::size_t nFileIndex)
{
const wchar_t* wszFileName = vecFileNames[nFileIndex];
wchar_t wszFilePath[MAX_PATH];
CRT::StringCat(CRT::StringCopy(wszFilePath, wszConfigurationsPath), wszFileName);
#if defined(CS_CONFIGURATION_BINARY)
if (BIN::SaveFile(wszFilePath))
#elif defined(CS_CONFIGURATION_JSON)
if (JSON::SaveFile(wszFilePath))
#elif defined(CS_CONFIGURATION_TOML)
if (TOML::SaveFile(wszFilePath))
#endif
{
L_PRINT(LOG_INFO) << CS_XOR("saved configuration file: \"") << wszFileName << CS_XOR("\"");
return true;
}
L_PRINT(LOG_WARNING) << CS_XOR("failed to save configuration file: \"") << wszFileName << CS_XOR("\"");
return false;
}
bool C::LoadFile(const std::size_t nFileIndex)
{
const wchar_t* wszFileName = vecFileNames[nFileIndex];
wchar_t wszFilePath[MAX_PATH];
CRT::StringCat(CRT::StringCopy(wszFilePath, wszConfigurationsPath), wszFileName);
#if defined(CS_CONFIGURATION_BINARY)
if (BIN::LoadFile(wszFilePath))
#elif defined(CS_CONFIGURATION_JSON)
if (JSON::LoadFile(wszFilePath))
#elif defined(CS_CONFIGURATION_TOML)
if (TOML::LoadFile(wszFilePath))
#endif
{
L_PRINT(LOG_INFO) << CS_XOR("loaded configuration file: \"") << wszFileName << CS_XOR("\"");
return true;
}
L_PRINT(LOG_WARNING) << CS_XOR("failed to load configuration file: \"") << wszFileName << CS_XOR("\"");
return false;
}
void C::RemoveFile(const std::size_t nFileIndex)
{
const wchar_t* wszFileName = vecFileNames[nFileIndex];
// unable to delete default config
if (CRT::StringCompare(wszFileName, CS_XOR(CS_CONFIGURATION_DEFAULT_FILE_NAME CS_CONFIGURATION_FILE_EXTENSION)) == 0)
{
L_PRINT(LOG_WARNING) << CS_XOR("unable to remove default configuration file: \"") << wszFileName << CS_XOR("\"");
return;
}
wchar_t wszFilePath[MAX_PATH];
CRT::StringCat(CRT::StringCopy(wszFilePath, wszConfigurationsPath), wszFileName);
if (::DeleteFileW(wszFilePath))
{
// erase and free filename from the list
vecFileNames.erase(vecFileNames.cbegin() + nFileIndex);
L_PRINT(LOG_INFO) << CS_XOR("removed configuration file: \"") << wszFileName << CS_XOR("\"");
}
}
#pragma endregion
#pragma region config_get
std::size_t C::GetVariableIndex(const FNV1A_t uNameHash)
{
for (std::size_t i = 0U; i < vecVariables.size(); i++)
{
if (vecVariables[i].uNameHash == uNameHash)
return i;
}
return C_INVALID_VARIABLE;
}
#pragma endregion
#pragma region config_user_types
void ColorPickerVar_t::UpdateRainbow()
{
// @todo: improve + optimize this code
// progress rainbow color
if (this->bRainbow)
{
const float flTime = static_cast<float>(ImGui::GetTime());
// create a rainbow color with copied alpha
float arrRainbowColors[] = {
sin(flTime * this->flRainbowSpeed) * 0.5f + 0.5f,
sin(flTime * this->flRainbowSpeed * MATH::_PI / 3) * 0.5f + 0.5f,
sin(flTime * this->flRainbowSpeed * MATH::_PI / 3) * 0.5f + 0.5f,
this->colValue.Base<COLOR_A>()
};
// set the rainbow color
this->colValue = Color_t::FromBase4(arrRainbowColors);
}
}

View File

@@ -0,0 +1,443 @@
#pragma once
// used: [stl] vector
#include <vector>
// used: [stl] type_info
#include <typeinfo>
// used: [win] undname_no_arguments
#include <dbghelp.h>
#include "../common.h"
#include "../sdk/datatypes/color.h"
// used: l_print
#include "../utilities/log.h"
// used: heapalloc, heapfree
#include "../utilities/memory.h"
// used: fnv1a hashing
#include "../utilities/fnv1a.h"
#pragma region config_definitions
#define C_ADD_VARIABLE(TYPE, NAME, DEFAULT) const std::size_t NAME = C::AddVariable<TYPE>(FNV1A::HashConst(#NAME), FNV1A::HashConst(#TYPE), DEFAULT);
#define C_ADD_VARIABLE_ARRAY(TYPE, SIZE, NAME, DEFAULT) const std::size_t NAME = C::AddVariableArray<TYPE[SIZE]>(FNV1A::HashConst(#NAME), FNV1A::HashConst(#TYPE "[]"), DEFAULT);
#define C_ADD_VARIABLE_ARRAY_ARRAY(TYPE, SIZE, SUBSIZE, NAME, DEFAULT) const std::size_t NAME = C::AddVariableArray<TYPE[SIZE][SUBSIZE]>(FNV1A::HashConst(#NAME), FNV1A::HashConst(#TYPE "[][]"), DEFAULT);
#define C_INVALID_VARIABLE static_cast<std::size_t>(-1)
#define C_GET(TYPE, NAME) C::Get<TYPE>(NAME)
#define C_GET_ARRAY(TYPE, SIZE, NAME, INDEX) C::Get<TYPE[SIZE]>(NAME)[INDEX]
#pragma endregion
#pragma region config_user_types
enum class EKeyBindMode : int
{
HOLD = 0,
TOGGLE
};
struct KeyBind_t
{
constexpr KeyBind_t(const char* szName, const unsigned int uKey = 0U, const EKeyBindMode nMode = EKeyBindMode::HOLD) :
szName(szName), uKey(uKey), nMode(nMode) { }
bool bEnable = false;
const char* szName = nullptr;
unsigned int uKey = 0U;
EKeyBindMode nMode = EKeyBindMode::HOLD;
};
struct ColorPickerVar_t
{
// default constructor
constexpr ColorPickerVar_t(const Color_t& colValue = Color_t(255, 255, 255), const bool bRainbow = false, const float flRainbowSpeed = 0.5f) :
colValue(colValue), bRainbow(bRainbow), flRainbowSpeed(flRainbowSpeed) { }
// @note: other contructors will only construct Color_t object and set rainbow to false and speed to 0.5f
// 8-bit color constructor (in: [0 .. 255])
constexpr ColorPickerVar_t(const std::uint8_t r, const std::uint8_t g, const std::uint8_t b, const std::uint8_t a = 255) :
colValue(r, g, b, a), bRainbow(false), flRainbowSpeed(0.5f) { }
// 8-bit color constructor (in: [0 .. 255])
constexpr ColorPickerVar_t(const int r, const int g, const int b, const int a = 255) :
colValue(r, g, b, a), bRainbow(false), flRainbowSpeed(0.5f) { }
// 8-bit array color constructor (in: [0.0 .. 1.0])
explicit constexpr ColorPickerVar_t(const std::uint8_t arrColor[4]) :
colValue(arrColor), bRainbow(false), flRainbowSpeed(0.5f) { }
// 32-bit packed color constructor (in: 0x00000000 - 0xFFFFFFFF)
explicit constexpr ColorPickerVar_t(const ImU32 uPackedColor) :
colValue(uPackedColor), bRainbow(false), flRainbowSpeed(0.5f) { }
// 32-bit color constructor (in: [0.0 .. 1.0])
constexpr ColorPickerVar_t(const float r, const float g, const float b, const float a = 1.0f) :
colValue(r, g, b, a), bRainbow(false), flRainbowSpeed(0.5f) { }
void UpdateRainbow();
Color_t colValue = Color_t(255, 255, 255);
bool bRainbow = false;
float flRainbowSpeed = 0.5f;
};
/// hold config variables for text component overlay
struct TextOverlayVar_t
{
constexpr TextOverlayVar_t(const bool bEnable, const float flThickness = 1.f, const Color_t& colPrimary = Color_t(255, 255, 255), const Color_t& colOutline = Color_t(0, 0, 0)) :
bEnable(bEnable), flThickness(flThickness), colPrimary(colPrimary), colOutline(colOutline) { }
bool bEnable = false;
float flThickness = 1.f;
Color_t colPrimary = Color_t(255, 255, 255);
Color_t colOutline = Color_t(0, 0, 0);
};
/// hold config variables for frame/box component overlay
struct FrameOverlayVar_t
{
constexpr FrameOverlayVar_t(const bool bEnable, const float flThickness = 1.f, const float flRounding = 0.f, const Color_t& colPrimary = Color_t(255, 255, 255), const Color_t& colOutline = Color_t(0, 0, 0)) :
bEnable(bEnable), flThickness(flThickness), flRounding(flRounding), colPrimary(colPrimary), colOutline(colOutline) { }
bool bEnable = false;
float flThickness = 1.f;
float flRounding = 0.f;
Color_t colPrimary = Color_t(255, 255, 255);
Color_t colOutline = Color_t(0, 0, 0);
};
/// hold config variables for bar component overlay
struct BarOverlayVar_t
{
constexpr BarOverlayVar_t(const bool bEnable, const bool bGradient = false, const bool bUseFactorColor = false, const float flThickness = 1.f, const Color_t& colPrimary = Color_t(255, 255, 255), const Color_t& colSecondary = Color_t(255, 255, 255), const Color_t& colBackground = Color_t(), const Color_t& colOutline = Color_t()) :
bEnable(bEnable), bGradient(bGradient), bUseFactorColor(bUseFactorColor), flThickness(flThickness), colPrimary(colPrimary), colSecondary(colSecondary), colBackground(colBackground), colOutline(colOutline) { }
bool bEnable = false;
bool bGradient = false;
bool bUseFactorColor = false;
float flThickness = 1.f;
Color_t colPrimary = Color_t(255, 255, 255);
Color_t colSecondary = Color_t(255, 255, 255);
Color_t colBackground = Color_t{};
Color_t colOutline = Color_t{};
};
#pragma endregion
/*
* CONFIGURATION
* - cheat variables serialization/de-serialization manager
*/
namespace C
{
// member of user-defined custom serialization structure
struct UserDataMember_t
{
// @todo: not sure is it possible and how todo this with projections, so currently done with pointer-to-member thing, probably could be optimized
template <typename T, typename C>
constexpr UserDataMember_t(const FNV1A_t uNameHash, const FNV1A_t uTypeHash, const T C::*pMember) :
uNameHash(uNameHash), uTypeHash(uTypeHash), nDataSize(sizeof(std::remove_pointer_t<T>)), uBaseOffset(reinterpret_cast<std::size_t>(std::addressof(static_cast<C*>(nullptr)->*pMember))) { } // @test: 'CS_OFFSETOF' must expand to the same result but for some reason it doesn't
// hash of custom variable name
FNV1A_t uNameHash = 0U;
// hash of custom variable type
FNV1A_t uTypeHash = 0U;
// data size of custom variable type
std::size_t nDataSize = 0U;
// offset to the custom variable from the base of class
std::size_t uBaseOffset = 0U;
};
// user-defined custom serialization structure
struct UserDataType_t
{
[[nodiscard]] std::size_t GetSerializationSize() const;
FNV1A_t uTypeHash = 0U;
std::vector<UserDataMember_t> vecMembers = {};
};
// variable info and value storage holder
struct VariableObject_t
{
// @test: it's required value to be either trivially copyable or allocated/copied by new/placement-new operators, otherwise it may cause UB
template <typename T> requires (!std::is_void_v<T> && std::is_trivially_copyable_v<T>)
VariableObject_t(const FNV1A_t uNameHash, const FNV1A_t uTypeHash, const T& valueDefault) :
uNameHash(uNameHash), uTypeHash(uTypeHash), nStorageSize(sizeof(T))
{
#ifndef CS_NO_RTTI
// store RTTI address if available
this->pTypeInfo = &typeid(std::remove_cvref_t<T>);
#endif
// @todo: do not call setstorage, instead construct it by placement-new operator
// allocate storage on the heap if it doesnt't fit on the local one
if constexpr (sizeof(T) > sizeof(this->storage.uLocal))
this->storage.pHeap = MEM::HeapAlloc(this->nStorageSize);
SetStorage(&valueDefault);
}
VariableObject_t(VariableObject_t&& other) noexcept :
uNameHash(other.uNameHash), uTypeHash(other.uTypeHash), nStorageSize(other.nStorageSize)
{
#ifndef CS_NO_RTTI
this->pTypeInfo = other.pTypeInfo;
#endif
if (this->nStorageSize <= sizeof(this->storage.uLocal))
CRT::MemoryCopy(&this->storage.uLocal, &other.storage.uLocal, sizeof(this->storage.uLocal));
else
{
this->storage.pHeap = other.storage.pHeap;
// prevent it from being freed when the moved object is destroyed
other.storage.pHeap = nullptr;
}
}
VariableObject_t(const VariableObject_t& other) :
uNameHash(other.uNameHash), uTypeHash(other.uTypeHash), nStorageSize(other.nStorageSize)
{
#ifndef CS_NO_RTTI
this->pTypeInfo = other.pTypeInfo;
#endif
if (this->nStorageSize <= sizeof(this->storage.uLocal))
CRT::MemoryCopy(&this->storage.uLocal, &other.storage.uLocal, sizeof(this->storage.uLocal));
else if (other.storage.pHeap != nullptr)
{
this->storage.pHeap = MEM::HeapAlloc(this->nStorageSize);
CRT::MemoryCopy(this->storage.pHeap, other.storage.pHeap, this->nStorageSize);
}
}
~VariableObject_t()
{
// check if heap memory is in use and allocated
if (this->nStorageSize > sizeof(this->storage.uLocal) && this->storage.pHeap != nullptr)
MEM::HeapFree(this->storage.pHeap);
}
VariableObject_t& operator=(VariableObject_t&& other) noexcept
{
// check if heap memory is in use and allocated
if (this->nStorageSize > sizeof(this->storage.uLocal) && this->storage.pHeap != nullptr)
MEM::HeapFree(this->storage.pHeap);
this->uNameHash = other.uNameHash;
this->uTypeHash = other.uTypeHash;
this->nStorageSize = other.nStorageSize;
#ifndef CS_NO_RTTI
this->pTypeInfo = other.pTypeInfo;
#endif
if (this->nStorageSize <= sizeof(this->storage.uLocal))
CRT::MemoryCopy(&this->storage.uLocal, &other.storage.uLocal, sizeof(this->storage.uLocal));
else
{
this->storage.pHeap = other.storage.pHeap;
// prevent it from being freed when the moved object is destroyed
other.storage.pHeap = nullptr;
}
return *this;
}
VariableObject_t& operator=(const VariableObject_t& other)
{
// check if heap memory is in use and allocated
if (this->nStorageSize > sizeof(this->storage.uLocal) && this->storage.pHeap != nullptr)
MEM::HeapFree(this->storage.pHeap);
this->uNameHash = other.uNameHash;
this->uTypeHash = other.uTypeHash;
this->nStorageSize = other.nStorageSize;
#ifndef CS_NO_RTTI
this->pTypeInfo = other.pTypeInfo;
#endif
if (this->nStorageSize <= sizeof(this->storage.uLocal))
CRT::MemoryCopy(&this->storage.uLocal, &other.storage.uLocal, sizeof(this->storage.uLocal));
else if (other.storage.pHeap != nullptr)
{
this->storage.pHeap = MEM::HeapAlloc(this->nStorageSize);
CRT::MemoryCopy(this->storage.pHeap, other.storage.pHeap, this->nStorageSize);
}
return *this;
}
/// @tparam bTypeSafe if true, activates additional comparison of source and requested type information, requires RTTI
/// @returns: pointer to the value storage, null if @a'bTypeSafe' is active and the access type does not match the variable type
template <typename T, bool bTypeSafe = true> requires (std::is_object_v<T>)
[[nodiscard]] const T* GetStorage() const
{
#ifndef CS_NO_RTTI
// sanity check of stored value type and asked value type
if constexpr (bTypeSafe)
{
if (const std::type_info& currentTypeInfo = typeid(std::remove_cvref_t<T>); this->pTypeInfo != nullptr && CRT::StringCompare(this->pTypeInfo->raw_name(), currentTypeInfo.raw_name()) != 0)
{
if (char szPresentTypeName[64] = {}, szAccessTypeName[64] = {};
MEM::fnUnDecorateSymbolName(this->pTypeInfo->raw_name() + 1U, szPresentTypeName, CS_ARRAYSIZE(szPresentTypeName), UNDNAME_NO_ARGUMENTS) != 0UL &&
MEM::fnUnDecorateSymbolName(currentTypeInfo.raw_name() + 1U, szAccessTypeName, CS_ARRAYSIZE(szAccessTypeName), UNDNAME_NO_ARGUMENTS) != 0UL)
{
L_PRINT(LOG_ERROR) << CS_XOR("accessing variable of type: \"") << szPresentTypeName << CS_XOR("\" with wrong type: \"") << szAccessTypeName << CS_XOR("\"");
}
CS_ASSERT(false); // storage value and asked data type mismatch
return nullptr;
}
}
#endif
// check is value stored in the local storage
if (this->nStorageSize <= sizeof(this->storage.uLocal))
return reinterpret_cast<const std::remove_cvref_t<T>*>(&this->storage.uLocal);
// otherwise it is allocated in the heap memory
CS_ASSERT(this->storage.pHeap != nullptr); // tried to access non allocated storage
return static_cast<const std::remove_cvref_t<T>*>(this->storage.pHeap);
}
template <typename T, bool bTypeSafe = true> requires (std::is_object_v<T>)
[[nodiscard]] T* GetStorage()
{
return const_cast<T*>(static_cast<const VariableObject_t*>(this)->GetStorage<T, bTypeSafe>());
}
// replace variable contained value
void SetStorage(const void* pValue);
/// @returns: the size of the data to be serialized/de-serialized into/from the configuration file
[[nodiscard]] std::size_t GetSerializationSize() const;
// hash of variable name
FNV1A_t uNameHash = 0x0;
// hash of value type
FNV1A_t uTypeHash = 0x0;
#ifndef CS_NO_RTTI
// address of RTTI type data for value type
const std::type_info* pTypeInfo = nullptr;
#endif
// value storage size in bytes
std::size_t nStorageSize = 0U;
// value storage
union
{
void* pHeap;
std::uint8_t uLocal[sizeof(std::uintptr_t)]; // @test: expand local storage size to fit max possible size of trivial type so we can minimize heap allocations count
} storage = { nullptr };
};
// create directories and default configuration file
bool Setup(const wchar_t* wszDefaultFileName);
/* @section: main */
// loop through directory content and store all user configurations filenames
void Refresh();
/// register user-defined data structure type and it's member variables
/// @param[in] vecUserMembers member variables of structure that needs to be serialized/de-serialized
void AddUserType(const FNV1A_t uTypeHash, std::initializer_list<UserDataMember_t> vecUserMembers);
/// write/re-write single variable to existing configuration file
/// @returns: true if variable has been found or created and successfully written, false otherwise
bool SaveFileVariable(const std::size_t nFileIndex, const VariableObject_t& variable);
/// read single variable from existing configuration file
/// @remarks: when the version of cheat is greater than version of the configuration file and @a'variable' wasn't found, this function saves it and updates the version to the current one, note that it doesn't affect to return value
/// @returns: true if variable has been found and successfully read, false otherwise
bool LoadFileVariable(const std::size_t nFileIndex, VariableObject_t& variable);
/// erase single variable from existing configuration file
/// @returns: true if variable did not exist or was successfully removed, false otherwise
bool RemoveFileVariable(const std::size_t nFileIndex, const VariableObject_t& variable);
/// create a new configuration file and save it
/// @param[in] wszFileName file name of configuration file to save and write in
/// @returns: true if file has been successfully created and all variables were written to it, false otherwise
bool CreateFile(const wchar_t* wszFileName);
/// serialize variables into the configuration file
/// @param[in] nFileIndex index of the exist configuration file name
/// @returns: true if all variables were successfully written to the file, false otherwise
bool SaveFile(const std::size_t nFileIndex);
/// de-serialize variables from the configuration file
/// @param[in] nFileIndex index of the exist configuration file name
/// @returns: true if all variables were successfully loaded from the file, false otherwise
bool LoadFile(const std::size_t nFileIndex);
/// remove configuration file
/// @param[in] nFileIndex index of the exist configuration file name
void RemoveFile(const std::size_t nFileIndex);
/* @section: values */
// all user configuration filenames
inline std::vector<wchar_t*> vecFileNames = {};
// custom user-defined serialization data types
inline std::vector<UserDataType_t> vecUserTypes = {};
// configuration variables storage
inline std::vector<VariableObject_t> vecVariables = {};
/* @section: get */
/// @returns: index of variable with given name hash if it exist, 'C_INVALID_VARIABLE' otherwise
[[nodiscard]] std::size_t GetVariableIndex(const FNV1A_t uNameHash);
/// @tparam T type of variable we're going to get, must be exactly the same as when registered
/// @returns: variable value at given index
template <typename T>
[[nodiscard]] T& Get(const std::size_t nIndex)
{
return *vecVariables[nIndex].GetStorage<T>();
}
// @todo: get rid of templates, so it doesn't compile duplicates and we're able to merge things to .cpp
/// add new configuration variable
/// @returns: index of added variable
template <typename T> requires (!std::is_array_v<T>)
std::size_t AddVariable(const FNV1A_t uNameHash, const FNV1A_t uTypeHash, const T& valueDefault)
{
vecVariables.emplace_back(uNameHash, uTypeHash, valueDefault);
return vecVariables.size() - 1U;
}
/// add new configuration array variable initialized by single value
/// @returns: index of added array variable
template <typename T> requires (std::is_array_v<T>)
std::size_t AddVariableArray(const FNV1A_t uNameHash, const FNV1A_t uTypeHash, const std::remove_pointer_t<std::decay_t<T>> valueDefault)
{
using BaseType_t = std::remove_pointer_t<std::decay_t<T>>;
T arrValueDefault;
for (std::size_t i = 0U; i < sizeof(T) / sizeof(BaseType_t); i++)
arrValueDefault[i] = valueDefault;
vecVariables.emplace_back(uNameHash, uTypeHash, arrValueDefault);
return vecVariables.size() - 1U;
}
/// add new configuration array variable with multiple values initialized
/// @returns: index of added array variable
template <typename T> requires (std::is_array_v<T>)
std::size_t AddVariableArray(const FNV1A_t uNameHash, const FNV1A_t uTypeHash, std::initializer_list<std::remove_pointer_t<std::decay_t<T>>> vecValuesDefault)
{
using BaseType_t = std::remove_pointer_t<std::decay_t<T>>;
T arrValueDefault;
CRT::MemorySet(arrValueDefault, 0U, sizeof(T));
CRT::MemoryCopy(arrValueDefault, vecValuesDefault.begin(), vecValuesDefault.size() * sizeof(BaseType_t));
vecVariables.emplace_back(uNameHash, uTypeHash, arrValueDefault);
return vecVariables.size() - 1U;
}
inline void RemoveVariable(const std::size_t nIndex)
{
vecVariables.erase(vecVariables.begin() + nIndex);
}
}

View File

@@ -0,0 +1,163 @@
// used: [stl] vector
#include <vector>
// used: [stl] find_if
#include <algorithm>
#include "convars.h"
// used: convar interface
#include "interfaces.h"
#include "../sdk/interfaces/ienginecvar.h"
// used: l_print
#include "../utilities/log.h"
// used: getworkingpath
#include "../core.h"
inline static void WriteConVarType(HANDLE hFile, const uint32_t nType)
{
switch ((EConVarType)nType)
{
case EConVarType_Bool:
::WriteFile(hFile, CS_XOR("[bool] "), CRT::StringLength(CS_XOR("[bool] ")), nullptr, nullptr);
break;
case EConVarType_Int16:
::WriteFile(hFile, CS_XOR("[int16] "), CRT::StringLength(CS_XOR("[int16] ")), nullptr, nullptr);
break;
case EConVarType_UInt16:
::WriteFile(hFile, CS_XOR("[uint16] "), CRT::StringLength(CS_XOR("[uint16] ")), nullptr, nullptr);
break;
case EConVarType_Int32:
::WriteFile(hFile, CS_XOR("[int32] "), CRT::StringLength(CS_XOR("[int32] ")), nullptr, nullptr);
break;
case EConVarType_UInt32:
::WriteFile(hFile, CS_XOR("[uint32] "), CRT::StringLength(CS_XOR("[uint32] ")), nullptr, nullptr);
break;
case EConVarType_Int64:
::WriteFile(hFile, CS_XOR("[int64] "), CRT::StringLength(CS_XOR("[int64] ")), nullptr, nullptr);
break;
case EConVarType_UInt64:
::WriteFile(hFile, CS_XOR("[uint64] "), CRT::StringLength(CS_XOR("[uint64] ")), nullptr, nullptr);
break;
case EConVarType_Float32:
::WriteFile(hFile, CS_XOR("[float32] "), CRT::StringLength(CS_XOR("[float32] ")), nullptr, nullptr);
break;
case EConVarType_Float64:
::WriteFile(hFile, CS_XOR("[float64] "), CRT::StringLength(CS_XOR("[float64] ")), nullptr, nullptr);
break;
case EConVarType_String:
::WriteFile(hFile, CS_XOR("[string] "), CRT::StringLength(CS_XOR("[string] ")), nullptr, nullptr);
break;
case EConVarType_Color:
::WriteFile(hFile, CS_XOR("[color] "), CRT::StringLength(CS_XOR("[color] ")), nullptr, nullptr);
break;
case EConVarType_Vector2:
::WriteFile(hFile, CS_XOR("[vector2] "), CRT::StringLength(CS_XOR("[vector2] ")), nullptr, nullptr);
break;
case EConVarType_Vector3:
::WriteFile(hFile, CS_XOR("[vector3] "), CRT::StringLength(CS_XOR("[vector3] ")), nullptr, nullptr);
break;
case EConVarType_Vector4:
::WriteFile(hFile, CS_XOR("[vector4] "), CRT::StringLength(CS_XOR("[vector4] ")), nullptr, nullptr);
break;
case EConVarType_Qangle:
::WriteFile(hFile, CS_XOR("[qangle] "), CRT::StringLength(CS_XOR("[qangle] ")), nullptr, nullptr);
break;
default:
::WriteFile(hFile, CS_XOR("[unknown-type] "), CRT::StringLength(CS_XOR("[unknown-type] ")), nullptr, nullptr);
break;
}
}
inline static void WriteConVarFlags(HANDLE hFile, const uint32_t nFlags)
{
if (nFlags & FCVAR_CLIENTDLL)
::WriteFile(hFile, CS_XOR("[client.dll] "), CRT::StringLength(CS_XOR("[client.dll] ")), nullptr, nullptr);
else if (nFlags & FCVAR_GAMEDLL)
::WriteFile(hFile, CS_XOR("[games's dll] "), CRT::StringLength(CS_XOR("[games's dll] ")), nullptr, nullptr);
if (nFlags & FCVAR_PROTECTED)
::WriteFile(hFile, CS_XOR("[protected] "), CRT::StringLength(CS_XOR("[protected] ")), nullptr, nullptr);
if (nFlags & FCVAR_CHEAT)
::WriteFile(hFile, CS_XOR("[cheat] "), CRT::StringLength(CS_XOR("[cheat] ")), nullptr, nullptr);
if (nFlags & FCVAR_HIDDEN)
::WriteFile(hFile, CS_XOR("[hidden] "), CRT::StringLength(CS_XOR("[hidden] ")), nullptr, nullptr);
if (nFlags & FCVAR_DEVELOPMENTONLY)
::WriteFile(hFile, CS_XOR("[devonly] "), CRT::StringLength(CS_XOR("[devonly] ")), nullptr, nullptr);
::WriteFile(hFile, CS_XOR("\n"), CRT::StringLength(CS_XOR("\n")), nullptr, nullptr);
}
bool CONVAR::Dump(const wchar_t* wszFileName)
{
wchar_t wszDumpFilePath[MAX_PATH];
if (!CORE::GetWorkingPath(wszDumpFilePath))
return false;
CRT::StringCat(wszDumpFilePath, wszFileName);
HANDLE hOutFile = ::CreateFileW(wszDumpFilePath, GENERIC_WRITE, FILE_SHARE_READ, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hOutFile == INVALID_HANDLE_VALUE)
return false;
// @todo: maybe remove this redundant? and put it inside CRT::String_t c'tor
const std::time_t time = std::time(nullptr);
std::tm timePoint;
localtime_s(&timePoint, &time);
CRT::String_t<64> szTimeBuffer(CS_XOR("[%d-%m-%Y %T] asphyxia | convars dump\n\n"), &timePoint);
// write current date, time and info
::WriteFile(hOutFile, szTimeBuffer.Data(), szTimeBuffer.Length(), nullptr, nullptr);
for (int i = I::Cvar->listConvars.Head(); i != I::Cvar->listConvars.InvalidIndex(); i = I::Cvar->listConvars.Next(i))
{
CConVar* pConVar = I::Cvar->listConvars.Element(i);
if (pConVar != nullptr)
{
// dump to file
WriteConVarType(hOutFile, pConVar->nType);
CRT::String_t<526> szBuffer(CS_XOR("%s : \"%s\" "), pConVar->szName, pConVar->szDescription[0] == '\0' ? CS_XOR("no description") : pConVar->szDescription);
::WriteFile(hOutFile, szBuffer.Data(), szBuffer.Length(), nullptr, nullptr);
// write flags
WriteConVarFlags(hOutFile, pConVar->nFlags);
}
}
::CloseHandle(hOutFile);
return true;
}
bool CONVAR::Setup()
{
bool bSuccess = true;
m_pitch = I::Cvar->Find(FNV1A::HashConst("m_pitch"));
bSuccess &= m_pitch != nullptr;
m_yaw = I::Cvar->Find(FNV1A::HashConst("m_yaw"));
bSuccess &= m_yaw != nullptr;
sensitivity = I::Cvar->Find(FNV1A::HashConst("sensitivity"));
bSuccess &= sensitivity != nullptr;
game_type = I::Cvar->Find(FNV1A::HashConst("game_type"));
bSuccess &= game_type != nullptr;
game_mode = I::Cvar->Find(FNV1A::HashConst("game_mode"));
bSuccess &= game_mode != nullptr;
mp_teammates_are_enemies = I::Cvar->Find(FNV1A::HashConst("mp_teammates_are_enemies"));
bSuccess &= mp_teammates_are_enemies != nullptr;
sv_autobunnyhopping = I::Cvar->Find(FNV1A::HashConst("sv_autobunnyhopping"));
bSuccess &= sv_autobunnyhopping != nullptr;
return bSuccess;
}

View File

@@ -0,0 +1,22 @@
#pragma once
class CConVar;
namespace CONVAR
{
// dump convars to file
bool Dump(const wchar_t* wszFileName);
// setup convars
bool Setup();
inline CConVar* m_pitch = nullptr;
inline CConVar* m_yaw = nullptr;
inline CConVar* sensitivity = nullptr;
inline CConVar* game_type = nullptr;
inline CConVar* game_mode = nullptr;
inline CConVar* mp_teammates_are_enemies = nullptr;
inline CConVar* sv_autobunnyhopping = nullptr;
}

View File

@@ -0,0 +1,319 @@
#include "hooks.h"
// used: variables
#include "variables.h"
// used: game's sdk
#include "../sdk/interfaces/iswapchaindx11.h"
#include "../sdk/interfaces/iviewrender.h"
#include "../sdk/interfaces/cgameentitysystem.h"
#include "../sdk/interfaces/ccsgoinput.h"
#include "../sdk/interfaces/iinputsystem.h"
#include "../sdk/interfaces/iengineclient.h"
#include "../sdk/interfaces/inetworkclientservice.h"
#include "../sdk/interfaces/iglobalvars.h"
#include "../sdk/interfaces/imaterialsystem.h"
#include "../sdk/interfaces/ipvs.h"
// used: viewsetup
#include "../sdk/datatypes/viewsetup.h"
// used: entity
#include "../sdk/entity.h"
// used: get virtual function, find pattern, ...
#include "../utilities/memory.h"
// used: inputsystem
#include "../utilities/inputsystem.h"
// used: draw
#include "../utilities/draw.h"
// used: features callbacks
#include "../features.h"
// used: CRC rebuild
#include "../features/CRC.h"
// used: game's interfaces
#include "interfaces.h"
#include "sdk.h"
// used: menu
#include "menu.h"
bool H::Setup()
{
if (MH_Initialize() != MH_OK)
{
L_PRINT(LOG_ERROR) << CS_XOR("failed to initialize minhook");
return false;
}
L_PRINT(LOG_INFO) << CS_XOR("minhook initialization completed");
if (!hkPresent.Create(MEM::GetVFunc(I::SwapChain->pDXGISwapChain, VTABLE::D3D::PRESENT), reinterpret_cast<void*>(&Present)))
return false;
L_PRINT(LOG_INFO) << CS_XOR("\"Present\" hook has been created");
if (!hkResizeBuffers.Create(MEM::GetVFunc(I::SwapChain->pDXGISwapChain, VTABLE::D3D::RESIZEBUFFERS), reinterpret_cast<void*>(&ResizeBuffers)))
return false;
L_PRINT(LOG_INFO) << CS_XOR("\"ResizeBuffers\" hook has been created");
// creat swap chain hook
IDXGIDevice* pDXGIDevice = NULL;
I::Device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice));
IDXGIAdapter* pDXGIAdapter = NULL;
pDXGIDevice->GetAdapter(&pDXGIAdapter);
IDXGIFactory* pIDXGIFactory = NULL;
pDXGIAdapter->GetParent(IID_PPV_ARGS(&pIDXGIFactory));
if (!hkCreateSwapChain.Create(MEM::GetVFunc(pIDXGIFactory, VTABLE::DXGI::CREATESWAPCHAIN), reinterpret_cast<void*>(&CreateSwapChain)))
return false;
L_PRINT(LOG_INFO) << CS_XOR("\"CreateSwapChain\" hook has been created");
pDXGIDevice->Release();
pDXGIDevice = nullptr;
pDXGIAdapter->Release();
pDXGIAdapter = nullptr;
pIDXGIFactory->Release();
pIDXGIFactory = nullptr;
// @ida: class CViewRender->OnRenderStart call GetMatricesForView
if (!hkGetMatrixForView.Create(MEM::FindPattern(CLIENT_DLL, CS_XOR("40 53 48 81 EC ? ? ? ? 49 8B C1")), reinterpret_cast<void*>(&GetMatrixForView)))
return false;
L_PRINT(LOG_INFO) << CS_XOR("\"GetMatrixForView\" hook has been created");
// @ida: #STR: cl: CreateMove clamped invalid attack history index %d in frame history to -1. Was %d, frame history size %d.\n
// Consider updating I::Input, VTABLE::CLIENT::CREATEMOVE and using that instead.
// For now, we'll use the pattern
// Credit: https://www.unknowncheats.me/forum/4265695-post6331.html
if (!hkCreateMove.Create(MEM::FindPattern(CLIENT_DLL, CS_XOR("48 8B C4 4C 89 40 ? 48 89 48 ? 55 53 56 57 48 8D A8")), reinterpret_cast<void*>(&CreateMove)))
return false;
L_PRINT(LOG_INFO) << CS_XOR("\"CreateMove\" hook has been created");
if (!hkMouseInputEnabled.Create(MEM::GetVFunc(I::Input, VTABLE::CLIENT::MOUSEINPUTENABLED), reinterpret_cast<void*>(&MouseInputEnabled)))
return false;
L_PRINT(LOG_INFO) << CS_XOR("\"MouseInputEnabled\" hook has been created");
if (!hkFrameStageNotify.Create(MEM::GetVFunc(I::Client, VTABLE::CLIENT::FRAMESTAGENOTIFY), reinterpret_cast<void*>(&FrameStageNotify)))
return false;
L_PRINT(LOG_INFO) << CS_XOR("\"FrameStageNotify\" hook has been created");
// in ida it will go in order as
// @ida: #STR: ; "game_newmap"
// @ida: #STR: ; "mapname"
// @ida: #STR: ; "transition"
// and the pattern is in the first one "game_newmap"
if (!hkLevelInit.Create(MEM::FindPattern(CLIENT_DLL, CS_XOR("48 89 5C 24 ? 56 48 83 EC ? 48 8B 0D ? ? ? ? 48 8B F2")), reinterpret_cast<void*>(&LevelInit)))
return false;
L_PRINT(LOG_INFO) << CS_XOR("\"LevelInit\" hook has been created");
// @ida: ClientModeShared -> #STR: "map_shutdown"
if (!hkLevelShutdown.Create(MEM::FindPattern(CLIENT_DLL, CS_XOR("48 83 EC ? 48 8B 0D ? ? ? ? 48 8D 15 ? ? ? ? 45 33 C9 45 33 C0 48 8B 01 FF 50 ? 48 85 C0 74 ? 48 8B 0D ? ? ? ? 48 8B D0 4C 8B 01 41 FF 50 ? 48 83 C4 28 E9 C3 20 01 ?")), reinterpret_cast<void*>(&LevelShutdown)))
return false;
L_PRINT(LOG_INFO) << CS_XOR("\"LevelShutdown\" hook has been created");
// @note: seems to do nothing for now...
// @ida: ClientModeCSNormal->OverrideView idx 15
//v21 = flSomeWidthSize * 0.5;
//v22 = *flSomeHeightSize * 0.5;
//*(float*)(pSetup + 0x49C) = v21; // m_OrthoRight
//*(float*)(pSetup + 0x494) = -v21; // m_OrthoLeft
//*(float*)(pSetup + 0x498) = -v22; // m_OrthoTop
//*(float*)(pSetup + 0x4A0) = v22; // m_OrthoBottom
if (!hkOverrideView.Create(MEM::FindPattern(CLIENT_DLL, CS_XOR("48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 41 56 41 57 48 83 EC ? 48 8B FA E8 20 1E ED FF")), reinterpret_cast<void*>(&OverrideView)))
return false;
//L_PRINT(LOG_INFO) << CS_XOR("\"OverrideView\" hook has been created");
// Credit: https://www.unknowncheats.me/forum/4253223-post6185.html
if (!hkDrawObject.Create(MEM::FindPattern(SCENESYSTEM_DLL, CS_XOR("48 8B C4 48 89 50 ? 53")), reinterpret_cast<void*>(&DrawObject)))
return false;
L_PRINT(LOG_INFO) << CS_XOR("\"DrawObject\" hook has been created");
if (!hkIsRelativeMouseMode.Create(MEM::GetVFunc(I::InputSystem, VTABLE::INPUTSYSTEM::ISRELATIVEMOUSEMODE), reinterpret_cast<void*>(&IsRelativeMouseMode)))
return false;
L_PRINT(LOG_INFO) << CS_XOR("\"IsRelativeMouseMode\" hook has been created");
return true;
}
void H::Destroy()
{
MH_DisableHook(MH_ALL_HOOKS);
MH_RemoveHook(MH_ALL_HOOKS);
MH_Uninitialize();
}
HRESULT __stdcall H::Present(IDXGISwapChain* pSwapChain, UINT uSyncInterval, UINT uFlags)
{
const auto oPresent = hkPresent.GetOriginal();
// recreate it if it's not valid
if (I::RenderTargetView == nullptr)
I::CreateRenderTarget();
// set our render target
if (I::RenderTargetView != nullptr)
I::DeviceContext->OMSetRenderTargets(1, &I::RenderTargetView, nullptr);
F::OnPresent();
return oPresent(I::SwapChain->pDXGISwapChain, uSyncInterval, uFlags);
}
HRESULT CS_FASTCALL H::ResizeBuffers(IDXGISwapChain* pSwapChain, std::uint32_t nBufferCount, std::uint32_t nWidth, std::uint32_t nHeight, DXGI_FORMAT newFormat, std::uint32_t nFlags)
{
const auto oResizeBuffer = hkResizeBuffers.GetOriginal();
auto hResult = oResizeBuffer(pSwapChain, nBufferCount, nWidth, nHeight, newFormat, nFlags);
if (SUCCEEDED(hResult))
I::CreateRenderTarget();
return hResult;
}
HRESULT __stdcall H::CreateSwapChain(IDXGIFactory* pFactory, IUnknown* pDevice, DXGI_SWAP_CHAIN_DESC* pDesc, IDXGISwapChain** ppSwapChain)
{
const auto oCreateSwapChain = hkCreateSwapChain.GetOriginal();
I::DestroyRenderTarget();
L_PRINT(LOG_INFO) << CS_XOR("render target view has been destroyed");
return oCreateSwapChain(pFactory, pDevice, pDesc, ppSwapChain);
}
long H::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (D::OnWndProc(hWnd, uMsg, wParam, lParam))
return 1L;
return ::CallWindowProcW(IPT::pOldWndProc, hWnd, uMsg, wParam, lParam);
}
ViewMatrix_t* CS_FASTCALL H::GetMatrixForView(CRenderGameSystem* pRenderGameSystem, IViewRender* pViewRender, ViewMatrix_t* pOutWorldToView, ViewMatrix_t* pOutViewToProjection, ViewMatrix_t* pOutWorldToProjection, ViewMatrix_t* pOutWorldToPixels)
{
const auto oGetMatrixForView = hkGetMatrixForView.GetOriginal();
ViewMatrix_t* matResult = oGetMatrixForView(pRenderGameSystem, pViewRender, pOutWorldToView, pOutViewToProjection, pOutWorldToProjection, pOutWorldToPixels);
// get view matrix
SDK::ViewMatrix = *pOutWorldToProjection;
// get camera position
// @note: ida @GetMatrixForView(global_pointer, pRenderGameSystem + 16, ...)
SDK::CameraPosition = pViewRender->vecOrigin;
return matResult;
}
bool CS_FASTCALL H::CreateMove(CCSGOInput* pInput, int nSlot, CUserCmd* cmd)
{
const auto oCreateMove = hkCreateMove.GetOriginal();
const bool bResult = oCreateMove(pInput, nSlot, cmd);
if (!I::Engine->IsConnected() || !I::Engine->IsInGame())
return bResult;
SDK::Cmd = cmd;
if (SDK::Cmd == nullptr)
return bResult;
CBaseUserCmdPB* pBaseCmd = SDK::Cmd->csgoUserCmd.pBaseCmd;
if (pBaseCmd == nullptr)
return bResult;
SDK::LocalController = CCSPlayerController::GetLocalPlayerController();
if (SDK::LocalController == nullptr)
return bResult;
SDK::LocalPawn = I::GameResourceService->pGameEntitySystem->Get<C_CSPlayerPawn>(SDK::LocalController->GetPawnHandle());
if (SDK::LocalPawn == nullptr)
return bResult;
F::OnCreateMove(SDK::Cmd, pBaseCmd, SDK::LocalController);
// TODO : We need to fix CRC saving
//
// There seems to be an issue within CBasePB and the classes that derive it.
// So far, you may be unable to press specific keys such as crouch and automatic shooting.
// A dodgy fix would be to comment it out but it still doesn't fix the bhop etc.
CRC::Save(pBaseCmd);
if (CRC::CalculateCRC(pBaseCmd) == true)
CRC::Apply(SDK::Cmd);
return bResult;
}
bool CS_FASTCALL H::MouseInputEnabled(void* pThisptr)
{
const auto oMouseInputEnabled = hkMouseInputEnabled.GetOriginal();
return MENU::bMainWindowOpened ? false : oMouseInputEnabled(pThisptr);
}
void CS_FASTCALL H::FrameStageNotify(void* rcx, int nFrameStage)
{
const auto oFrameStageNotify = hkFrameStageNotify.GetOriginal();
F::OnFrameStageNotify(nFrameStage);
return oFrameStageNotify(rcx, nFrameStage);
}
__int64* CS_FASTCALL H::LevelInit(void* pClientModeShared, const char* szNewMap)
{
const auto oLevelInit = hkLevelInit.GetOriginal();
// if global variables are not captured during I::Setup or we join a new game, recapture it
if (I::GlobalVars == nullptr)
I::GlobalVars = *reinterpret_cast<IGlobalVars**>(MEM::ResolveRelativeAddress(MEM::FindPattern(CLIENT_DLL, CS_XOR("48 8B 0D 99 C7 0D 01 4C 8D 05 42 CB 0D 01")), 0x3, 0x7));
// disable model occlusion
I::PVS->Set(false);
return oLevelInit(pClientModeShared, szNewMap);
}
__int64 CS_FASTCALL H::LevelShutdown(void* pClientModeShared)
{
const auto oLevelShutdown = hkLevelShutdown.GetOriginal();
// reset global variables since it got discarded by the game
I::GlobalVars = nullptr;
return oLevelShutdown(pClientModeShared);
}
void CS_FASTCALL H::OverrideView(void* pClientModeCSNormal, CViewSetup* pSetup)
{
const auto oOverrideView = hkOverrideView.GetOriginal();
if (!I::Engine->IsConnected() || !I::Engine->IsInGame())
return hkOverrideView.GetOriginal()(pClientModeCSNormal, pSetup);
oOverrideView(pClientModeCSNormal, pSetup);
}
void CS_FASTCALL H::DrawObject(void* pAnimatableSceneObjectDesc, void* pDx11, CMeshData* arrMeshDraw, int nDataCount, void* pSceneView, void* pSceneLayer, void* pUnk, void* pUnk2)
{
const auto oDrawObject = hkDrawObject.GetOriginal();
if (!I::Engine->IsConnected() || !I::Engine->IsInGame())
return oDrawObject(pAnimatableSceneObjectDesc, pDx11, arrMeshDraw, nDataCount, pSceneView, pSceneLayer, pUnk, pUnk2);
if (SDK::LocalController == nullptr || SDK::LocalPawn == nullptr)
return oDrawObject(pAnimatableSceneObjectDesc, pDx11, arrMeshDraw, nDataCount, pSceneView, pSceneLayer, pUnk, pUnk2);
if (!F::OnDrawObject(pAnimatableSceneObjectDesc, pDx11, arrMeshDraw, nDataCount, pSceneView, pSceneLayer, pUnk, pUnk2))
oDrawObject(pAnimatableSceneObjectDesc, pDx11, arrMeshDraw, nDataCount, pSceneView, pSceneLayer, pUnk, pUnk2);
}
void* H::IsRelativeMouseMode(void* pThisptr, bool bActive)
{
const auto oIsRelativeMouseMode = hkIsRelativeMouseMode.GetOriginal();
MENU::bMainActive = bActive;
if (MENU::bMainWindowOpened)
return oIsRelativeMouseMode(pThisptr, false);
return oIsRelativeMouseMode(pThisptr, bActive);
}

View File

@@ -0,0 +1,97 @@
#pragma once
// used: [d3d] api
#include <d3d11.h>
#include <dxgi1_2.h>
// used: chookobject
#include "../utilities/detourhook.h"
// used: viewmatrix_t
#include "../sdk/datatypes/matrix.h"
namespace VTABLE
{
namespace D3D
{
enum
{
PRESENT = 8U,
RESIZEBUFFERS = 13U,
RESIZEBUFFERS_CSTYLE = 39U,
};
}
namespace DXGI
{
enum
{
CREATESWAPCHAIN = 10U,
};
}
namespace CLIENT
{
enum
{
CREATEMOVE = 5U,
MOUSEINPUTENABLED = 16U,
FRAMESTAGENOTIFY = 36U,
};
}
namespace INPUTSYSTEM
{
enum
{
ISRELATIVEMOUSEMODE = 78U,
};
}
}
class CRenderGameSystem;
class IViewRender;
class CCSGOInput;
class CViewSetup;
class CMeshData;
namespace H
{
bool Setup();
void Destroy();
/* @section: handlers */
// d3d11 & wndproc
HRESULT WINAPI Present(IDXGISwapChain* pSwapChain, UINT uSyncInterval, UINT uFlags);
HRESULT CS_FASTCALL ResizeBuffers(IDXGISwapChain* pSwapChain, std::uint32_t nBufferCount, std::uint32_t nWidth, std::uint32_t nHeight, DXGI_FORMAT newFormat, std::uint32_t nFlags);
HRESULT WINAPI CreateSwapChain(IDXGIFactory* pFactory, IUnknown* pDevice, DXGI_SWAP_CHAIN_DESC* pDesc, IDXGISwapChain** ppSwapChain);
long CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
// game's functions
ViewMatrix_t* CS_FASTCALL GetMatrixForView(CRenderGameSystem* pRenderGameSystem, IViewRender* pViewRender, ViewMatrix_t* pOutWorldToView, ViewMatrix_t* pOutViewToProjection, ViewMatrix_t* pOutWorldToProjection, ViewMatrix_t* pOutWorldToPixels);
bool CS_FASTCALL CreateMove(CCSGOInput* pInput, int nSlot, bool bActive);
bool CS_FASTCALL MouseInputEnabled(void* pThisptr);
void CS_FASTCALL FrameStageNotify(void* rcx, int nFrameStage);
__int64* CS_FASTCALL LevelInit(void* pClientModeShared, const char* szNewMap);
__int64 CS_FASTCALL LevelShutdown(void* pClientModeShared);
void CS_FASTCALL OverrideView(void* pClientModeCSNormal, CViewSetup* pSetup);
void CS_FASTCALL DrawObject(void* pAnimatableSceneObjectDesc, void* pDx11, CMeshData* arrMeshDraw, int nDataCount, void* pSceneView, void* pSceneLayer, void* pUnk, void* pUnk2);
void* IsRelativeMouseMode(void* pThisptr, bool bActive);
/* @section: managers */
inline CBaseHookObject<decltype(&Present)> hkPresent = {};
inline CBaseHookObject<decltype(&ResizeBuffers)> hkResizeBuffers = {};
inline CBaseHookObject<decltype(&CreateSwapChain)> hkCreateSwapChain = {};
inline CBaseHookObject<decltype(&WndProc)> hkWndProc = {};
inline CBaseHookObject<decltype(&GetMatrixForView)> hkGetMatrixForView = {};
inline CBaseHookObject<decltype(&CreateMove)> hkCreateMove = {};
inline CBaseHookObject<decltype(&MouseInputEnabled)> hkMouseInputEnabled = {};
inline CBaseHookObject<decltype(&IsRelativeMouseMode)> hkIsRelativeMouseMode = {};
inline CBaseHookObject<decltype(&FrameStageNotify)> hkFrameStageNotify = {};
inline CBaseHookObject<decltype(&LevelInit)> hkLevelInit = {};
inline CBaseHookObject<decltype(&LevelShutdown)> hkLevelShutdown = {};
inline CBaseHookObject<decltype(&OverrideView)> hkOverrideView = {};
inline CBaseHookObject<decltype(&DrawObject)> hkDrawObject = {};
}

View File

@@ -0,0 +1,251 @@
// used: [d3d] api
#include <d3d11.h>
#include "interfaces.h"
// used: findpattern, callvirtual, getvfunc...
#include "../utilities/memory.h"
// used: l_print
#include "../utilities/log.h"
// used: iswapchaindx11
#include "../sdk/interfaces/iswapchaindx11.h"
#include "../sdk/interfaces/iresourcesystem.h"
#pragma region interfaces_get
using InstantiateInterfaceFn_t = void* (*)();
class CInterfaceRegister
{
public:
InstantiateInterfaceFn_t fnCreate;
const char* szName;
CInterfaceRegister* pNext;
};
static const CInterfaceRegister* GetRegisterList(const wchar_t* wszModuleName)
{
void* hModule = MEM::GetModuleBaseHandle(wszModuleName);
if (hModule == nullptr)
return nullptr;
std::uint8_t* pCreateInterface = reinterpret_cast<std::uint8_t*>(MEM::GetExportAddress(hModule, CS_XOR("CreateInterface")));
if (pCreateInterface == nullptr)
{
L_PRINT(LOG_ERROR) << CS_XOR("failed to get \"CreateInterface\" address");
return nullptr;
}
return *reinterpret_cast<CInterfaceRegister**>(MEM::ResolveRelativeAddress(pCreateInterface, 0x3, 0x7));
}
template <typename T = void*>
T* Capture(const CInterfaceRegister* pModuleRegister, const char* szInterfaceName)
{
for (const CInterfaceRegister* pRegister = pModuleRegister; pRegister != nullptr; pRegister = pRegister->pNext)
{
if (const std::size_t nInterfaceNameLength = CRT::StringLength(szInterfaceName);
// found needed interface
CRT::StringCompareN(szInterfaceName, pRegister->szName, nInterfaceNameLength) == 0 &&
// and we've given full name with hardcoded digits
(CRT::StringLength(pRegister->szName) == nInterfaceNameLength ||
// or it contains digits after name
CRT::StringToInteger<int>(pRegister->szName + nInterfaceNameLength, nullptr, 10) > 0))
{
// capture our interface
void* pInterface = pRegister->fnCreate();
#ifdef _DEBUG
// log interface address
L_PRINT(LOG_INFO) << CS_XOR("captured \"") << pRegister->szName << CS_XOR("\" interface at address: ") << L::AddFlags(LOG_MODE_INT_SHOWBASE | LOG_MODE_INT_FORMAT_HEX) << reinterpret_cast<std::uintptr_t>(pInterface);
#else
L_PRINT(LOG_INFO) << CS_XOR("captured \"") << pRegister->szName << CS_XOR("\" interface");
#endif
return static_cast<T*>(pInterface);
}
}
L_PRINT(LOG_ERROR) << CS_XOR("failed to find interface \"") << szInterfaceName << CS_XOR("\"");
return nullptr;
}
#pragma endregion
bool I::Setup()
{
bool bSuccess = true;
#pragma region interface_game_exported
const auto pTier0Handle = MEM::GetModuleBaseHandle(TIER0_DLL);
if (pTier0Handle == nullptr)
return false;
MemAlloc = *reinterpret_cast<IMemAlloc**>(MEM::GetExportAddress(pTier0Handle, CS_XOR("g_pMemAlloc")));
bSuccess &= (MemAlloc != nullptr);
const auto pSchemaSystemRegisterList = GetRegisterList(SCHEMASYSTEM_DLL);
if (pSchemaSystemRegisterList == nullptr)
return false;
SchemaSystem = Capture<ISchemaSystem>(pSchemaSystemRegisterList, SCHEMA_SYSTEM);
bSuccess &= (SchemaSystem != nullptr);
const auto pInputSystemRegisterList = GetRegisterList(INPUTSYSTEM_DLL);
if (pInputSystemRegisterList == nullptr)
return false;
InputSystem = Capture<IInputSystem>(pInputSystemRegisterList, INPUT_SYSTEM_VERSION);
bSuccess &= (InputSystem != nullptr);
const auto pEngineRegisterList = GetRegisterList(ENGINE2_DLL);
if (pEngineRegisterList == nullptr)
return false;
GameResourceService = Capture<IGameResourceService>(pEngineRegisterList, GAME_RESOURCE_SERVICE_CLIENT);
bSuccess &= (GameResourceService != nullptr);
Engine = Capture<IEngineClient>(pEngineRegisterList, SOURCE2_ENGINE_TO_CLIENT);
bSuccess &= (Engine != nullptr);
NetworkClientService = Capture<INetworkClientService>(pEngineRegisterList, NETWORK_CLIENT_SERVICE);
bSuccess &= (NetworkClientService != nullptr);
const auto pTier0RegisterList = GetRegisterList(TIER0_DLL);
if (pTier0RegisterList == nullptr)
return false;
Cvar = Capture<IEngineCVar>(pTier0RegisterList, ENGINE_CVAR);
bSuccess &= (Cvar != nullptr);
const auto pClientRegister = GetRegisterList(CLIENT_DLL);
if (pClientRegister == nullptr)
return false;
Client = Capture<ISource2Client>(pClientRegister, SOURCE2_CLIENT);
bSuccess &= (Client != nullptr);
const auto pMaterialSystem2Register = GetRegisterList(MATERIAL_SYSTEM2_DLL);
if (pMaterialSystem2Register == nullptr)
return false;
MaterialSystem2 = Capture<IMaterialSystem2>(pMaterialSystem2Register, MATERIAL_SYSTEM2);
bSuccess &= (MaterialSystem2 != nullptr);
const auto pResourceSystemRegisterList = GetRegisterList(RESOURCESYSTEM_DLL);
if (pResourceSystemRegisterList == nullptr)
return false;
ResourceSystem = Capture<IResourceSystem>(pResourceSystemRegisterList, RESOURCE_SYSTEM);
bSuccess &= (ResourceSystem != nullptr);
if (ResourceSystem != nullptr)
{
ResourceHandleUtils = reinterpret_cast<CResourceHandleUtils*>(ResourceSystem->QueryInterface(RESOURCE_HANDLE_UTILS));
bSuccess &= (ResourceHandleUtils != nullptr);
}
#pragma endregion
// @ida: #STR: "r_gpu_mem_stats", "-threads", "CTSListBase: Misaligned list\n", "CTSQueue: Misaligned queue\n", "Display GPU memory usage.", "-r_max_device_threads"
SwapChain = **reinterpret_cast<ISwapChainDx11***>(MEM::ResolveRelativeAddress(MEM::FindPattern(RENDERSYSTEM_DLL, CS_XOR("66 0F 7F 0D ? ? ? ? 66 0F 7F 05 ? ? ? ? 0F 1F 40")), 0x4, 0x8));
bSuccess &= (SwapChain != nullptr);
// grab's d3d11 interfaces for later use
if (SwapChain != nullptr)
{
if (FAILED(SwapChain->pDXGISwapChain->GetDevice(__uuidof(ID3D11Device), (void**)&Device)))
{
L_PRINT(LOG_ERROR) << CS_XOR("failed to get device from swapchain");
CS_ASSERT(false);
return false;
}
else
// we successfully got device, so we can get immediate context
Device->GetImmediateContext(&DeviceContext);
}
bSuccess &= (Device != nullptr && DeviceContext != nullptr);
Input = *reinterpret_cast<CCSGOInput**>(MEM::ResolveRelativeAddress(MEM::FindPattern(CLIENT_DLL, CS_XOR("48 8B 0D ? ? ? ? E8 ? ? ? ? 8B BE 84 12 00 00")), 0x3, 0x7));
bSuccess &= (Input != nullptr);
// @ida: STR '%s: %f tick(%d) curtime(%f) OnSequenceCycleChanged: %s : %d=[%s]'
GlobalVars = *reinterpret_cast<IGlobalVars**>(MEM::ResolveRelativeAddress(MEM::FindPattern(CLIENT_DLL, CS_XOR("48 89 0D ? ? ? ? 48 89 41")), 0x3, 0x7));
bSuccess &= (GlobalVars != nullptr);
PVS = reinterpret_cast<CPVS*>(MEM::ResolveRelativeAddress(MEM::FindPattern(ENGINE2_DLL, CS_XOR("48 8D 0D ? ? ? ? 33 D2 FF 50")), 0x3, 0x7));
bSuccess &= (PVS != nullptr);
GameTraceManager = *reinterpret_cast<CGameTraceManager**>(MEM::GetAbsoluteAddress(MEM::FindPattern(CLIENT_DLL, CS_XOR("4C 8B 3D ? ? ? ? 24 C9 0C 49 66 0F 7F 45")), 0x3, 0x0));
bSuccess &= (GameTraceManager != nullptr);
return bSuccess;
}
void I::CreateRenderTarget()
{
if (FAILED(SwapChain->pDXGISwapChain->GetDevice(__uuidof(ID3D11Device), (void**)&Device)))
{
L_PRINT(LOG_ERROR) << CS_XOR("failed to get device from swapchain");
CS_ASSERT(false);
}
else
// we successfully got device, so we can get immediate context
Device->GetImmediateContext(&DeviceContext);
// @note: i dont use this anywhere else so lambda is fine
static const auto GetCorrectDXGIFormat = [](DXGI_FORMAT eCurrentFormat)
{
switch (eCurrentFormat)
{
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
return DXGI_FORMAT_R8G8B8A8_UNORM;
}
return eCurrentFormat;
};
DXGI_SWAP_CHAIN_DESC sd;
SwapChain->pDXGISwapChain->GetDesc(&sd);
ID3D11Texture2D* pBackBuffer = nullptr;
if (SUCCEEDED(SwapChain->pDXGISwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer))))
{
if (pBackBuffer)
{
D3D11_RENDER_TARGET_VIEW_DESC desc{};
desc.Format = static_cast<DXGI_FORMAT>(GetCorrectDXGIFormat(sd.BufferDesc.Format));
desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
if (FAILED(Device->CreateRenderTargetView(pBackBuffer, &desc, &RenderTargetView)))
{
L_PRINT(LOG_WARNING) << CS_XOR("failed to create render target view with D3D11_RTV_DIMENSION_TEXTURE2D...");
L_PRINT(LOG_INFO) << CS_XOR("retrying to create render target view with D3D11_RTV_DIMENSION_TEXTURE2DMS...");
desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;
if (FAILED(Device->CreateRenderTargetView(pBackBuffer, &desc, &RenderTargetView)))
{
L_PRINT(LOG_WARNING) << CS_XOR("failed to create render target view with D3D11_RTV_DIMENSION_TEXTURE2D...");
L_PRINT(LOG_INFO) << CS_XOR("retrying...");
if (FAILED(Device->CreateRenderTargetView(pBackBuffer, NULL, &RenderTargetView)))
{
L_PRINT(LOG_ERROR) << CS_XOR("failed to create render target view");
CS_ASSERT(false);
}
}
}
pBackBuffer->Release();
pBackBuffer = nullptr;
}
}
}
void I::DestroyRenderTarget()
{
if (RenderTargetView != nullptr)
{
RenderTargetView->Release();
RenderTargetView = nullptr;
}
}

View File

@@ -0,0 +1,79 @@
#pragma once
#include "../common.h"
// used: globalvariables
#include "../sdk/interfaces/iglobalvars.h"
#pragma region sdk_definitons
#define GAME_RESOURCE_SERVICE_CLIENT CS_XOR("GameResourceServiceClientV00")
#define SOURCE2_CLIENT CS_XOR("Source2Client00")
#define SCHEMA_SYSTEM CS_XOR("SchemaSystem_00")
#define INPUT_SYSTEM_VERSION CS_XOR("InputSystemVersion00")
#define SOURCE2_ENGINE_TO_CLIENT CS_XOR("Source2EngineToClient00")
#define ENGINE_CVAR CS_XOR("VEngineCvar00")
#define LOCALIZE CS_XOR("Localize_00")
#define NETWORK_CLIENT_SERVICE CS_XOR("NetworkClientService_00")
#define MATERIAL_SYSTEM2 CS_XOR("VMaterialSystem2_00")
#define RESOURCE_SYSTEM CS_XOR("ResourceSystem013")
#define RESOURCE_HANDLE_UTILS CS_XOR("ResourceHandleUtils001")
// @source: master/game/shared/shareddefs.h
#define TICK_INTERVAL 0.015625f
#define TIME_TO_TICKS(TIME) (static_cast<int>(0.5f + static_cast<float>(TIME) / TICK_INTERVAL))
#define TICKS_TO_TIME(TICKS) (TICK_INTERVAL * static_cast<float>(TICKS))
#define ROUND_TO_TICKS(TIME) (TICK_INTERVAL * TIME_TO_TICKS(TIME))
#define TICK_NEVER_THINK (-1)
#pragma endregion
// game interfaces
class ISwapChainDx11;
class IMemAlloc;
class CCSGOInput;
class ISchemaSystem;
class IInputSystem;
class IGameResourceService;
class ISource2Client;
class IEngineClient;
class IEngineCVar;
class INetworkClientService;
class IMaterialSystem2;
class IResourceSystem;
class CResourceHandleUtils;
class CPVS;
class CGameTraceManager;
// [d3d] struct
struct ID3D11Device;
struct ID3D11DeviceContext;
struct ID3D11RenderTargetView;
namespace I
{
bool Setup();
/* @section: helpers */
// create and destroy render target view for handling resize
void CreateRenderTarget();
void DestroyRenderTarget();
inline IMemAlloc* MemAlloc = nullptr;
inline ISwapChainDx11* SwapChain = nullptr;
inline ID3D11Device* Device = nullptr;
inline ID3D11DeviceContext* DeviceContext = nullptr;
inline ID3D11RenderTargetView* RenderTargetView = nullptr;
inline CCSGOInput* Input = nullptr;
inline ISchemaSystem* SchemaSystem = nullptr;
inline IGlobalVars* GlobalVars = nullptr;
inline IInputSystem* InputSystem = nullptr;
inline IGameResourceService* GameResourceService = nullptr;
inline ISource2Client* Client = nullptr;
inline IEngineClient* Engine = nullptr;
inline IEngineCVar* Cvar = nullptr;
inline INetworkClientService* NetworkClientService = nullptr;
inline IMaterialSystem2* MaterialSystem2 = nullptr;
inline IResourceSystem* ResourceSystem = nullptr;
inline CResourceHandleUtils* ResourceHandleUtils = nullptr;
inline CPVS* PVS = nullptr;
inline CGameTraceManager* GameTraceManager = nullptr;
}

View File

@@ -0,0 +1,115 @@
#pragma once
// used: [stl] vector
#include <vector>
#include "../common.h"
// used: [ext] imgui, draw, animation
#include "../utilities/draw.h"
#define MENU_MAX_BACKGROUND_PARTICLES 100
class CTab
{
public:
const char* szName;
void (*pRenderFunction)();
};
namespace MENU
{
void RenderMainWindow();
void RenderOverlayPreviewWindow();
void RenderWatermark();
void UpdateStyle(ImGuiStyle* pStyle = nullptr);
/* @section: particles */
struct ParticleData_t
{
ParticleData_t(const ImVec2& vecPosition, const ImVec2& vecVelocity) :
vecPosition(vecPosition), vecVelocity(vecVelocity) { }
// current particle position
ImVec2 vecPosition = {};
// current particle velocity
ImVec2 vecVelocity = {};
};
struct ParticleContext_t
{
ParticleContext_t(const int nMaxParticles = 100)
{
// allocate memory for particles
this->vecParticles.reserve(nMaxParticles);
// create particles if needed
}
~ParticleContext_t()
{
// since no memory allocated, just clear vector
this->vecParticles.clear();
}
void Render(ImDrawList* pDrawList, const ImVec2& vecScreenSize, const float flAlpha);
// create particle with random velocity/position
void AddParticle(const ImVec2& vecScreenSize);
// current size of particles
const size_t Count() const { return this->vecParticles.size(); }
private:
// draw particle (circle)
void DrawParticle(ImDrawList* pDrawList, ParticleData_t& particle, const Color_t& colPrimary);
// find & draw connection as a line between particles
void FindConnections(ImDrawList* pDrawList, ParticleData_t& particle, const Color_t& colPrimary, float flMaxDistance);
void DrawConnection(ImDrawList* pDrawList, ParticleData_t& particle, ParticleData_t& otherParticle, float flAlpha, const Color_t& colPrimary) const;
// update particle position/velocity
// reversed direction when particle is out of screen
void UpdatePosition(ParticleData_t& particle, const ImVec2& vecScreenSize) const;
void ResolveScreenCollision(ParticleData_t& particle, const ImVec2& vecScreenSize) const;
// all our particles data
std::vector<ParticleData_t> vecParticles;
};
inline bool bMainWindowOpened = false;
inline bool bMainActive = false;
inline int nCurrentMainTab = 0;
inline ParticleContext_t menuParticle = ParticleContext_t(MENU_MAX_BACKGROUND_PARTICLES);
inline AnimationHandler_t animMenuDimBackground;
inline float flDpiScale = 1.f;
}
namespace T
{
/* @section: main */
void Render(const char* szTabBar, const CTab* arrTabs, const unsigned long long nTabsCount, int* nCurrentTab, ImGuiTabBarFlags flags = ImGuiTabBarFlags_NoCloseWithMiddleMouseButton | ImGuiTabBarFlags_NoTooltip);
/* @section: tabs */
void RageBot();
void LegitBot();
void Visuals();
void Miscellaneous();
void SkinsChanger();
/* @section: values */
// user-defined configuration filename in miscellaneous tab
inline char szConfigFile[256U] = {};
// current selected configuration in miscellaneous tab
inline unsigned long long nSelectedConfig = ~1U;
// current sub tab overlay in visuals tab
inline int nCurrentOverlaySubtab = 0;
}
namespace menu
{
using namespace ImGui;
const ImGuiColorEditFlags color_edit4_flags = ImGuiColorEditFlags_NoBorder | ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoDragDrop | ImGuiColorEditFlags_AlphaPreview;
void render();
}

View File

@@ -0,0 +1,168 @@
#include "schema.h"
// used: [stl] vector
#include <vector>
// used: [stl] find_if
#include <algorithm>
// used: getworkingpath
#include "../core.h"
// used: ischemasystem
#include "interfaces.h"
#include "../sdk/interfaces/ischemasystem.h"
// used: l_print
#include "../utilities/log.h"
struct SchemaData_t
{
FNV1A_t uHashedFieldName = 0x0ULL;
std::uint32_t uOffset = 0x0U;
};
static std::vector<SchemaData_t> vecSchemaData;
bool SCHEMA::Setup(const wchar_t* wszFileName, const char* szModuleName)
{
wchar_t wszDumpFilePath[MAX_PATH];
if (!CORE::GetWorkingPath(wszDumpFilePath))
return false;
CRT::StringCat(wszDumpFilePath, wszFileName);
HANDLE hOutFile = ::CreateFileW(wszDumpFilePath, GENERIC_WRITE, FILE_SHARE_READ, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hOutFile == INVALID_HANDLE_VALUE)
return false;
// @todo: maybe remove this redundant? and put it inside CRT::String_t c'tor
const std::time_t time = std::time(nullptr);
std::tm timePoint;
localtime_s(&timePoint, &time);
CRT::String_t<64> szTimeBuffer(CS_XOR("[%d-%m-%Y %T] asphyxia | schema dump\n\n"), &timePoint);
// write current date, time and info
::WriteFile(hOutFile, szTimeBuffer.Data(), szTimeBuffer.Length(), nullptr, nullptr);
CSchemaSystemTypeScope* pTypeScope = I::SchemaSystem->FindTypeScopeForModule(szModuleName);
if (pTypeScope == nullptr)
return false;
const int nTableSize = pTypeScope->hashClasses.Count();
L_PRINT(LOG_INFO) << CS_XOR("found \"") << nTableSize << CS_XOR("\" schema classes in module");
// allocate memory for elements
UtlTSHashHandle_t* pElements = new UtlTSHashHandle_t[nTableSize + 1U];
const auto nElements = pTypeScope->hashClasses.GetElements(0, nTableSize, pElements);
for (int i = 0; i < nElements; i++)
{
const UtlTSHashHandle_t hElement = pElements[i];
if (hElement == 0)
continue;
CSchemaClassBinding* pClassBinding = pTypeScope->hashClasses[hElement];
if (pClassBinding == nullptr)
continue;
SchemaClassInfoData_t* pDeclaredClassInfo;
pTypeScope->FindDeclaredClass(&pDeclaredClassInfo, pClassBinding->szBinaryName);
if (pDeclaredClassInfo == nullptr)
continue;
if (pDeclaredClassInfo->nFieldSize == 0)
continue;
CRT::String_t<MAX_PATH> szClassBuffer(CS_XOR("class %s\n"), pDeclaredClassInfo->szName);
::WriteFile(hOutFile, szClassBuffer.Data(), szClassBuffer.Length(), nullptr, nullptr);
for (auto j = 0; j < pDeclaredClassInfo->nFieldSize; j++)
{
SchemaClassFieldData_t* pFields = pDeclaredClassInfo->pFields;
CRT::String_t<MAX_PATH> szFieldClassBuffer(CS_XOR("%s->%s"), pClassBinding->szBinaryName, pFields[j].szName);
// store field info
vecSchemaData.emplace_back(FNV1A::Hash(szFieldClassBuffer.Data()), pFields[j].nSingleInheritanceOffset);
CRT::String_t<MAX_PATH> szFieldBuffer(CS_XOR(" %s %s = 0x%X\n"), pFields[j].pSchemaType->szName, pFields[j].szName, pFields[j].nSingleInheritanceOffset);
// write field info
::WriteFile(hOutFile, szFieldBuffer.Data(), szFieldBuffer.Length(), nullptr, nullptr);
}
#ifdef _DEBUG
L_PRINT(LOG_INFO) << CS_XOR("dumped \"") << pDeclaredClassInfo->szName << CS_XOR("\" (total: ") << pDeclaredClassInfo->nFieldSize << CS_XOR(" fields)");
#endif
}
// free allocated memory
delete[] pElements;
// close file
::CloseHandle(hOutFile);
return true;
}
std::uint32_t SCHEMA::GetOffset(const FNV1A_t uHashedFieldName)
{
if (const auto it = std::ranges::find_if(vecSchemaData, [uHashedFieldName](const SchemaData_t& data)
{ return data.uHashedFieldName == uHashedFieldName; });
it != vecSchemaData.end())
return it->uOffset;
L_PRINT(LOG_ERROR) << CS_XOR("failed to find offset for field with hash: ") << L::AddFlags(LOG_MODE_INT_FORMAT_HEX | LOG_MODE_INT_SHOWBASE) << uHashedFieldName;
CS_ASSERT(false); // schema field not found
return 0U;
}
// @todo: optimize this, this is really poorly do and can be done much better?
std::uint32_t SCHEMA::GetForeignOffset(const char* szModulenName, const FNV1A_t uHashedClassName, const FNV1A_t uHashedFieldName)
{
CSchemaSystemTypeScope* pTypeScope = I::SchemaSystem->FindTypeScopeForModule(szModulenName);
if (pTypeScope == nullptr)
return false;
const int nTableSize = pTypeScope->hashClasses.Count();
// allocate memory for elements
UtlTSHashHandle_t* pElements = new UtlTSHashHandle_t[nTableSize + 1U];
const auto nElements = pTypeScope->hashClasses.GetElements(0, nTableSize, pElements);
std::uint32_t uOffset = 0x0;
for (int i = 0; i < nElements; i++)
{
const UtlTSHashHandle_t hElement = pElements[i];
if (hElement == 0)
continue;
CSchemaClassBinding* pClassBinding = pTypeScope->hashClasses[hElement];
if (pClassBinding == nullptr)
continue;
SchemaClassInfoData_t* pDeclaredClassInfo;
pTypeScope->FindDeclaredClass(&pDeclaredClassInfo, pClassBinding->szBinaryName);
if (pDeclaredClassInfo == nullptr)
continue;
if (pDeclaredClassInfo->nFieldSize == 0)
continue;
for (auto j = 0; j < pDeclaredClassInfo->nFieldSize; j++)
{
SchemaClassFieldData_t* pFields = pDeclaredClassInfo->pFields;
if (pFields == nullptr)
continue;
SchemaClassFieldData_t field = pFields[j];
if (FNV1A::Hash(pClassBinding->szBinaryName) == uHashedClassName && FNV1A::Hash(field.szName) == uHashedFieldName)
uOffset = field.nSingleInheritanceOffset;
}
}
if (uOffset == 0x0)
L_PRINT(LOG_WARNING) << CS_XOR("failed to find offset for field with hash: ") << L::AddFlags(LOG_MODE_INT_FORMAT_HEX | LOG_MODE_INT_SHOWBASE) << uHashedFieldName;
return uOffset;
}

View File

@@ -0,0 +1,45 @@
#pragma once
#include "../common.h"
// used: fnv-1a hash
#include "../utilities/fnv1a.h"
#define SCHEMA_ADD_OFFSET(TYPE, NAME, OFFSET) \
[[nodiscard]] CS_INLINE std::add_lvalue_reference_t<TYPE> NAME() \
{ \
static const std::uint32_t uOffset = OFFSET; \
return *reinterpret_cast<std::add_pointer_t<TYPE>>(reinterpret_cast<std::uint8_t*>(this) + (uOffset)); \
}
#define SCHEMA_ADD_POFFSET(TYPE, NAME, OFFSET) \
[[nodiscard]] CS_INLINE std::add_pointer_t<TYPE> NAME() \
{ \
const static std::uint32_t uOffset = OFFSET; \
return reinterpret_cast<std::add_pointer_t<TYPE>>(reinterpret_cast<std::uint8_t*>(this) + (uOffset)); \
}
#define SCHEMA_ADD_FIELD_OFFSET(TYPE, NAME, FIELD, ADDITIONAL) SCHEMA_ADD_OFFSET(TYPE, NAME, SCHEMA::GetOffset(FNV1A::HashConst(FIELD)) + ADDITIONAL)
#define SCHEMA_ADD_FIELD(TYPE, NAME, FIELD) SCHEMA_ADD_FIELD_OFFSET(TYPE, NAME, FIELD, 0U)
#define SCHEMA_ADD_PFIELD_OFFSET(TYPE, NAME, FIELD, ADDITIONAL) SCHEMA_ADD_POFFSET(TYPE, NAME, SCHEMA::GetOffset(FNV1A::HashConst(FIELD)) + ADDITIONAL)
#define SCHEMA_ADD_PFIELD(TYPE, NAME, FIELD) SCHEMA_ADD_PFIELD_OFFSET(TYPE, NAME, FIELD, 0U)
// @todo: dump enums?
namespace SCHEMA
{
// store the offset of the field in the class
// dump stored data to file
bool Setup(const wchar_t* wszFileName, const char* szModuleName);
/* @section: get */
// get offset of the field in the class
// @note: only client.dll class & fields
[[nodiscard]] std::uint32_t GetOffset(const FNV1A_t uHashedFieldName);
// get foregin offset from other .dll
[[nodiscard]] std::uint32_t GetForeignOffset(const char* szModulenName, const FNV1A_t uHashedClassName, const FNV1A_t uHashedFieldName);
}

View File

@@ -0,0 +1,18 @@
#include "sdk.h"
// used: getmodulebasehandle
#include "../utilities/memory.h"
bool SDK::Setup()
{
bool bSuccess = true;
const void* hTier0Lib = MEM::GetModuleBaseHandle(TIER0_DLL);
if (hTier0Lib == nullptr)
return false;
fnConColorMsg = reinterpret_cast<decltype(fnConColorMsg)>(MEM::GetExportAddress(hTier0Lib, CS_XOR("?ConColorMsg@@YAXAEBVColor@@PEBDZZ")));
bSuccess &= fnConColorMsg != nullptr;
return bSuccess;
}

View File

@@ -0,0 +1,36 @@
#pragma once
// used: viewmatrix_t
#include "../sdk/datatypes/matrix.h"
// used: color_t
#include "../sdk/datatypes/color.h"
// used: cmd
#include "../sdk/datatypes/usercmd.h"
#pragma region sdk_definitions
// @source: master/public/worldsize.h
// world coordinate bounds
#define MAX_COORD_FLOAT 16'384.f
#define MIN_COORD_FLOAT (-MAX_COORD_FLOAT)
// @source: master/public/vphysics_interface.h
// coordinates are in HL units. 1 unit == 1 inch
#define METERS_PER_INCH 0.0254f
#pragma endregion
class CCSPlayerController;
class C_CSPlayerPawn;
namespace SDK
{
// capture game's exported functions
bool Setup();
inline ViewMatrix_t ViewMatrix = ViewMatrix_t();
inline Vector_t CameraPosition = Vector_t();
inline CCSPlayerController* LocalController = nullptr;
inline C_CSPlayerPawn* LocalPawn = nullptr;
inline CUserCmd* Cmd = nullptr;
inline void(CS_CDECL* fnConColorMsg)(const Color_t&, const char*, ...) = nullptr;
}

View File

@@ -0,0 +1,132 @@
#pragma once
#include "config.h"
#pragma region variables_combo_entries
using VisualOverlayBox_t = int;
enum EVisualOverlayBox : VisualOverlayBox_t
{
VISUAL_OVERLAY_BOX_NONE = 0,
VISUAL_OVERLAY_BOX_FULL,
VISUAL_OVERLAY_BOX_CORNERS,
VISUAL_OVERLAY_BOX_MAX
};
using VisualChamMaterial_t = int;
enum EVisualsChamMaterials : VisualChamMaterial_t
{
VISUAL_MATERIAL_PRIMARY_WHITE = 0,
VISUAL_MATERIAL_ILLUMINATE,
VISUAL_MATERIAL_MAX
};
using MiscDpiScale_t = int;
enum EMiscDpiScale : MiscDpiScale_t
{
MISC_DPISCALE_DEFAULT = 0,
MISC_DPISCALE_125,
MISC_DPISCALE_150,
MISC_DPISCALE_175,
MISC_DPISCALE_200,
MISC_DPISCALE_MAX
};
#pragma endregion
#pragma region variables_multicombo_entries
using MenuAddition_t = unsigned int;
enum EMenuAddition : MenuAddition_t
{
MENU_ADDITION_NONE = 0U,
MENU_ADDITION_DIM_BACKGROUND = 1 << 0,
MENU_ADDITION_BACKGROUND_PARTICLE = 1 << 1,
MENU_ADDITION_GLOW = 1 << 2,
MENU_ADDITION_ALL = MENU_ADDITION_DIM_BACKGROUND | MENU_ADDITION_BACKGROUND_PARTICLE | MENU_ADDITION_GLOW
};
#pragma endregion
struct Variables_t
{
#pragma region variables_visuals
C_ADD_VARIABLE(bool, bVisualOverlay, false);
C_ADD_VARIABLE(FrameOverlayVar_t, overlayBox, FrameOverlayVar_t(false));
C_ADD_VARIABLE(TextOverlayVar_t, overlayName, TextOverlayVar_t(false));
C_ADD_VARIABLE(BarOverlayVar_t, overlayHealthBar, BarOverlayVar_t(false, false, false, 1.f, Color_t(0, 255, 0), Color_t(255, 0, 0)));
C_ADD_VARIABLE(BarOverlayVar_t, overlayArmorBar, BarOverlayVar_t(false, false, false, 1.f, Color_t(0, 255, 255), Color_t(255, 0, 0)));
C_ADD_VARIABLE(bool, bVisualChams, false);
C_ADD_VARIABLE(int, nVisualChamMaterial, VISUAL_MATERIAL_PRIMARY_WHITE);
C_ADD_VARIABLE(bool, bVisualChamsIgnoreZ, true); // invisible chams
C_ADD_VARIABLE(Color_t, colVisualChams, Color_t(0, 255, 0));
C_ADD_VARIABLE(Color_t, colVisualChamsIgnoreZ, Color_t(255, 0, 0));
#pragma endregion
#pragma region variables_misc
C_ADD_VARIABLE(bool, bAntiUntrusted, true);
C_ADD_VARIABLE(bool, bWatermark, true);
C_ADD_VARIABLE(bool, bAntiAim, false);
C_ADD_VARIABLE(bool, bAutoBHop, false);
C_ADD_VARIABLE(int, nAutoBHopChance, 100);
C_ADD_VARIABLE(bool, bAutoStrafe, false);
#pragma endregion
#pragma region variables_menu
C_ADD_VARIABLE(unsigned int, nMenuKey, VK_INSERT);
C_ADD_VARIABLE(unsigned int, nPanicKey, VK_END);
C_ADD_VARIABLE(int, nDpiScale, 0);
/*
* color navigation:
* [definition N][purpose]
* 1. primitive:
* - primtv 0 (text)
* - primtv 1 (background)
* - primtv 2 (disabled)
* - primtv 3 (control bg)
* - primtv 4 (border)
* - primtv 5 (hover)
*
* 2. accents:
* - accent 0 (main)
* - accent 1 (dark)
* - accent 2 (darker)
*/
C_ADD_VARIABLE(unsigned int, bMenuAdditional, MENU_ADDITION_ALL);
C_ADD_VARIABLE(float, flAnimationSpeed, 1.f);
C_ADD_VARIABLE(ColorPickerVar_t, colPrimtv0, ColorPickerVar_t(255, 255, 255)); // (text)
C_ADD_VARIABLE(ColorPickerVar_t, colPrimtv1, ColorPickerVar_t(50, 55, 70)); // (background)
C_ADD_VARIABLE(ColorPickerVar_t, colPrimtv2, ColorPickerVar_t(190, 190, 190)); // (disabled)
C_ADD_VARIABLE(ColorPickerVar_t, colPrimtv3, ColorPickerVar_t(20, 20, 30)); // (control bg)
C_ADD_VARIABLE(ColorPickerVar_t, colPrimtv4, ColorPickerVar_t(0, 0, 0)); // (border)
C_ADD_VARIABLE(ColorPickerVar_t, colAccent0, ColorPickerVar_t(85, 90, 160)); // (main)
C_ADD_VARIABLE(ColorPickerVar_t, colAccent1, ColorPickerVar_t(100, 105, 175)); // (dark)
C_ADD_VARIABLE(ColorPickerVar_t, colAccent2, ColorPickerVar_t(115, 120, 190)); // (darker)
#pragma endregion
#pragma region variables_legitbot
C_ADD_VARIABLE(bool, bLegitbot, false);
C_ADD_VARIABLE(bool, bShowRange, false);
C_ADD_VARIABLE(float, flAimRange, 10.0f);
C_ADD_VARIABLE(float, flSmoothing, 10.0f);
C_ADD_VARIABLE(bool, bSilentbot, false);
C_ADD_VARIABLE(float, flSilentRange, 1.0f);
C_ADD_VARIABLE(bool, bLegitbotAlwaysOn, false);
C_ADD_VARIABLE(unsigned int, nLegitbotActivationKey, VK_HOME);
C_ADD_VARIABLE(bool, bRCS, false);
C_ADD_VARIABLE(float, flRCSPitch, 0.0f);
C_ADD_VARIABLE(float, flRCSYaw, 0.0f);
C_ADD_VARIABLE(float, flRCSSmooth, 5.0f);
C_ADD_VARIABLE(bool, bTriggerbot, false);
C_ADD_VARIABLE(unsigned int, nTriggerbotActivationKey, VK_HOME);
C_ADD_VARIABLE(float, flTriggerbotDelay, 0.1f);
#pragma endregion
};
inline Variables_t Vars = {};