2025-07-29 20:23:06 +03:00

407 lines
13 KiB
C++

// 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);
}
}