mirror of
https://github.com/ineedbots/cod2m.git
synced 2025-04-19 16:02:53 +00:00
Overall cleanup
This commit is contained in:
parent
9564dcec13
commit
b35949ddaf
141
src/Components/Loader.cpp
Normal file
141
src/Components/Loader.cpp
Normal file
@ -0,0 +1,141 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
#include "Modules/Flags.hpp"
|
||||
#include "Modules/HelloWorld.hpp"
|
||||
#include "Modules/Logger.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
bool Loader::Pregame = true;
|
||||
bool Loader::Postgame = false;
|
||||
bool Loader::Uninitializing = false;
|
||||
std::vector<Component*> Loader::Components;
|
||||
|
||||
bool Loader::IsPregame()
|
||||
{
|
||||
return Loader::Pregame;
|
||||
}
|
||||
|
||||
bool Loader::IsPostgame()
|
||||
{
|
||||
return Loader::Postgame;
|
||||
}
|
||||
|
||||
bool Loader::IsUninitializing()
|
||||
{
|
||||
return Loader::Uninitializing;
|
||||
}
|
||||
|
||||
void Loader::Initialize(GAMEEXE GameType)
|
||||
{
|
||||
Loader::Pregame = true;
|
||||
Loader::Postgame = false;
|
||||
Loader::Uninitializing = false;
|
||||
Utils::Memory::GetAllocator()->clear();
|
||||
|
||||
Game::Init(GameType);
|
||||
Loader::Register(new Flags());
|
||||
Loader::Register(new HelloWorld());
|
||||
Loader::Register(new Logger());
|
||||
|
||||
Loader::Pregame = false;
|
||||
}
|
||||
|
||||
void Loader::Uninitialize()
|
||||
{
|
||||
Loader::Uninitializing = true;
|
||||
Loader::PreDestroyNoPostGame();
|
||||
|
||||
std::reverse(Loader::Components.begin(), Loader::Components.end());
|
||||
for (auto component : Loader::Components)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (!Loader::IsPerformingUnitTests())
|
||||
{
|
||||
Logger::Print("Unregistering component: %s\n", component->getName().data());
|
||||
}
|
||||
#endif
|
||||
delete component;
|
||||
}
|
||||
|
||||
Loader::Components.clear();
|
||||
Utils::Memory::GetAllocator()->clear();
|
||||
Loader::Uninitializing = false;
|
||||
}
|
||||
|
||||
void Loader::PreDestroy()
|
||||
{
|
||||
if (!Loader::Postgame)
|
||||
{
|
||||
Loader::Postgame = true;
|
||||
|
||||
auto components = Loader::Components;
|
||||
|
||||
std::reverse(components.begin(), components.end());
|
||||
for (auto component : components)
|
||||
{
|
||||
component->preDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Loader::PreDestroyNoPostGame()
|
||||
{
|
||||
if (!Loader::Postgame)
|
||||
{
|
||||
auto components = Loader::Components;
|
||||
|
||||
std::reverse(components.begin(), components.end());
|
||||
for (auto component : components)
|
||||
{
|
||||
component->preDestroy();
|
||||
}
|
||||
|
||||
Loader::Postgame = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Loader::PerformUnitTests()
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
Logger::Print("Performing unit tests for components:\n");
|
||||
|
||||
for (auto component : Loader::Components)
|
||||
{
|
||||
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
|
||||
Logger::Print("Testing '%s'...\n", component->getName().data());
|
||||
#endif
|
||||
auto startTime = std::chrono::high_resolution_clock::now();
|
||||
bool testRes = component->unitTest();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - startTime).count();
|
||||
Logger::Print("Test done (%llims): %s\n\n", duration, (testRes ? "Success" : "Error"));
|
||||
result &= testRes;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Loader::IsPerformingUnitTests()
|
||||
{
|
||||
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
|
||||
return Flags::HasFlag("tests");
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Loader::Register(Component* component)
|
||||
{
|
||||
if (component)
|
||||
{
|
||||
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
|
||||
if (!Loader::IsPerformingUnitTests())
|
||||
{
|
||||
Logger::Print("Component registered: %s\n", component->getName().data());
|
||||
}
|
||||
#endif
|
||||
Loader::Components.push_back(component);
|
||||
}
|
||||
}
|
||||
}
|
62
src/Components/Loader.hpp
Normal file
62
src/Components/Loader.hpp
Normal file
@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class Component
|
||||
{
|
||||
public:
|
||||
Component() {};
|
||||
virtual ~Component() {};
|
||||
|
||||
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
|
||||
virtual std::string getName()
|
||||
{
|
||||
std::string name = typeid(*this).name();
|
||||
Utils::String::Replace(name, "class Components::", "");
|
||||
return name;
|
||||
};
|
||||
#endif
|
||||
|
||||
// It's illegal to spawn threads in DLLMain, and apparently it causes problems if they are destroyed there as well.
|
||||
// This method is called before DLLMain (if possible) and should to destroy threads.
|
||||
// It's not 100% guaranteed that it's called outside DLLMain, as it depends on the game, but it's 100% guaranteed, that it is called at all.
|
||||
virtual void preDestroy() {};
|
||||
virtual bool unitTest() { return true; }; // Unit testing entry
|
||||
};
|
||||
|
||||
class Loader
|
||||
{
|
||||
public:
|
||||
static void Initialize(GAMEEXE);
|
||||
static void Uninitialize();
|
||||
static void PreDestroy();
|
||||
static void PreDestroyNoPostGame();
|
||||
static bool PerformUnitTests();
|
||||
static bool IsPerformingUnitTests();
|
||||
static void Register(Component* component);
|
||||
|
||||
static bool IsPregame();
|
||||
static bool IsPostgame();
|
||||
static bool IsUninitializing();
|
||||
|
||||
template <typename T>
|
||||
static T* GetInstance()
|
||||
{
|
||||
for (auto& component : Loader::Components)
|
||||
{
|
||||
if (typeid(*component) == typeid(T))
|
||||
{
|
||||
return reinterpret_cast<T*>(component);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool Pregame;
|
||||
static bool Postgame;
|
||||
static bool Uninitializing;
|
||||
static std::vector<Component*> Components;
|
||||
};
|
||||
}
|
56
src/Components/Modules/Flags.cpp
Normal file
56
src/Components/Modules/Flags.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
#include "STDInclude.hpp"
|
||||
#include "Flags.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
std::vector<std::string> Flags::EnabledFlags;
|
||||
|
||||
bool Flags::HasFlag(const std::string& flag)
|
||||
{
|
||||
Flags::ParseFlags();
|
||||
|
||||
for (auto entry : Flags::EnabledFlags)
|
||||
{
|
||||
if (Utils::String::ToLower(entry) == Utils::String::ToLower(flag))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Flags::ParseFlags()
|
||||
{
|
||||
static bool flagsParsed = false;
|
||||
if (flagsParsed) return;
|
||||
flagsParsed = true;
|
||||
|
||||
int numArgs;
|
||||
LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &numArgs);
|
||||
|
||||
if (argv)
|
||||
{
|
||||
for (int i = 0; i < numArgs; ++i)
|
||||
{
|
||||
std::wstring wFlag(argv[i]);
|
||||
if (wFlag[0] == L'-')
|
||||
{
|
||||
Flags::EnabledFlags.push_back(std::string(++wFlag.begin(), wFlag.end()));
|
||||
}
|
||||
}
|
||||
|
||||
LocalFree(argv);
|
||||
}
|
||||
}
|
||||
|
||||
Flags::Flags()
|
||||
{
|
||||
Flags::ParseFlags();
|
||||
}
|
||||
|
||||
Flags::~Flags()
|
||||
{
|
||||
Flags::EnabledFlags.clear();
|
||||
}
|
||||
}
|
18
src/Components/Modules/Flags.hpp
Normal file
18
src/Components/Modules/Flags.hpp
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class Flags : public Component
|
||||
{
|
||||
public:
|
||||
Flags();
|
||||
~Flags();
|
||||
|
||||
static bool HasFlag(const std::string& flag);
|
||||
|
||||
private:
|
||||
static std::vector<std::string> EnabledFlags;
|
||||
|
||||
static void ParseFlags();
|
||||
};
|
||||
}
|
13
src/Components/Modules/HelloWorld.cpp
Normal file
13
src/Components/Modules/HelloWorld.cpp
Normal file
@ -0,0 +1,13 @@
|
||||
#include "STDInclude.hpp"
|
||||
#include "HelloWorld.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
HelloWorld::HelloWorld()
|
||||
{
|
||||
}
|
||||
|
||||
HelloWorld::~HelloWorld()
|
||||
{
|
||||
}
|
||||
}
|
11
src/Components/Modules/HelloWorld.hpp
Normal file
11
src/Components/Modules/HelloWorld.hpp
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class HelloWorld : public Component
|
||||
{
|
||||
public:
|
||||
HelloWorld();
|
||||
~HelloWorld();
|
||||
};
|
||||
}
|
96
src/Components/Modules/Logger.cpp
Normal file
96
src/Components/Modules/Logger.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
#include "STDInclude.hpp"
|
||||
#include "Logger.hpp"
|
||||
#include "Flags.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
bool Logger::IsConsoleReady()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void Logger::PrintStub(int channel, const char* message, ...)
|
||||
{
|
||||
return Logger::MessagePrint(channel, Logger::Format(&message));
|
||||
}
|
||||
|
||||
void Logger::Print(const char* message, ...)
|
||||
{
|
||||
return Logger::MessagePrint(0, Logger::Format(&message));
|
||||
}
|
||||
|
||||
void Logger::Print(int channel, const char* message, ...)
|
||||
{
|
||||
return Logger::MessagePrint(channel, Logger::Format(&message));
|
||||
}
|
||||
|
||||
void Logger::MessagePrint([[maybe_unused]] int channel, [[maybe_unused]] const std::string& message)
|
||||
{
|
||||
if (Flags::HasFlag("stdout") || Loader::IsPerformingUnitTests())
|
||||
{
|
||||
printf("%s", message.data());
|
||||
fflush(stdout);
|
||||
return;
|
||||
}
|
||||
|
||||
/*if (!Logger::IsConsoleReady())
|
||||
{
|
||||
OutputDebugStringA(message.data());
|
||||
}
|
||||
|
||||
if (!Game::Sys_IsMainThread())
|
||||
{
|
||||
Logger::EnqueueMessage(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
Game::Com_PrintMessage(channel, message.data(), 0);
|
||||
}*/
|
||||
}
|
||||
|
||||
void Logger::ErrorPrint([[maybe_unused]] int error, [[maybe_unused]] const std::string& message)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (IsDebuggerPresent()) __debugbreak();
|
||||
#endif
|
||||
|
||||
//Game::Com_Error(error, "%s", message.data());
|
||||
}
|
||||
|
||||
void Logger::Error(int error, const char* message, ...)
|
||||
{
|
||||
return Logger::ErrorPrint(error, Logger::Format(&message));
|
||||
}
|
||||
|
||||
void Logger::Error(const char* message, ...)
|
||||
{
|
||||
return Logger::ErrorPrint(0, Logger::Format(&message));
|
||||
}
|
||||
|
||||
void Logger::SoftError(const char* message, ...)
|
||||
{
|
||||
return Logger::ErrorPrint(2, Logger::Format(&message));
|
||||
}
|
||||
|
||||
std::string Logger::Format(const char** message)
|
||||
{
|
||||
const size_t bufferSize = 0x10000;
|
||||
Utils::Memory::Allocator allocator;
|
||||
char* buffer = allocator.allocateArray<char>(bufferSize);
|
||||
|
||||
va_list ap = reinterpret_cast<char*>(const_cast<char**>(&message[1]));
|
||||
//va_start(ap, *message);
|
||||
_vsnprintf_s(buffer, bufferSize, bufferSize, *message, ap);
|
||||
va_end(ap);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
Logger::Logger()
|
||||
{
|
||||
}
|
||||
|
||||
Logger::~Logger()
|
||||
{
|
||||
}
|
||||
}
|
26
src/Components/Modules/Logger.hpp
Normal file
26
src/Components/Modules/Logger.hpp
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class Logger : public Component
|
||||
{
|
||||
public:
|
||||
Logger();
|
||||
~Logger();
|
||||
|
||||
static void MessagePrint(int channel, const std::string& message);
|
||||
static void Print(int channel, const char* message, ...);
|
||||
static void Print(const char* message, ...);
|
||||
static void ErrorPrint(int error, const std::string& message);
|
||||
static void Error(const char* message, ...);
|
||||
static void Error(int error, const char* message, ...);
|
||||
static void SoftError(const char* message, ...);
|
||||
static bool IsConsoleReady();
|
||||
|
||||
static void PrintStub(int channel, const char* message, ...);
|
||||
|
||||
private:
|
||||
|
||||
static std::string Format(const char** message);
|
||||
};
|
||||
}
|
10
src/Game/Game.cpp
Normal file
10
src/Game/Game.cpp
Normal file
@ -0,0 +1,10 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Game
|
||||
{
|
||||
void Init(GAMEEXE)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
6
src/Game/Game.hpp
Normal file
6
src/Game/Game.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace Game
|
||||
{
|
||||
void Init(GAMEEXE);
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace MP
|
||||
{
|
||||
void PatchT4()
|
||||
{
|
||||
MessageBoxA(nullptr, "MP", "DEBUG", 0);
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace MP
|
||||
{
|
||||
void PatchT4();
|
||||
}
|
@ -1 +0,0 @@
|
||||
#include "STDInclude.hpp"
|
5
src/Game/Structs.hpp
Normal file
5
src/Game/Structs.hpp
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace Game
|
||||
{
|
||||
}
|
@ -31,7 +31,7 @@ namespace Main
|
||||
// detect which executable's patches to apply
|
||||
DWORD dataStrData = Utils::Hook::Get<DWORD>(0x59B69C);
|
||||
if (dataStrData == 0x6C6C6143)
|
||||
MP::PatchT4();
|
||||
Components::Loader::Initialize(GAMEEXE::MP);
|
||||
|
||||
hModule = GetModuleHandle(NULL);
|
||||
PIMAGE_DOS_HEADER header = (PIMAGE_DOS_HEADER)hModule;
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <Psapi.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <Shlwapi.h>
|
||||
#include <assert.h>
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4091)
|
||||
@ -81,9 +82,19 @@ template <size_t S> class Sizer { };
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
#include "Utils/Hooking.hpp"
|
||||
enum GAMEEXE
|
||||
{
|
||||
MP
|
||||
};
|
||||
|
||||
#include "Game/MP.hpp"
|
||||
#include "Utils/Hooking.hpp"
|
||||
#include "Utils/Memory.hpp"
|
||||
#include "Utils/String.hpp"
|
||||
|
||||
#include "Game/Structs.hpp"
|
||||
#include "Game/Game.hpp"
|
||||
|
||||
#include "Components/Loader.hpp"
|
||||
|
||||
// Libraries
|
||||
#pragma comment(lib, "Winmm.lib")
|
||||
|
105
src/Utils/Memory.cpp
Normal file
105
src/Utils/Memory.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
Utils::Memory::Allocator Memory::MemAllocator;
|
||||
|
||||
void* Memory::AllocateAlign(size_t length, size_t alignment)
|
||||
{
|
||||
void* data = _aligned_malloc(length, alignment);
|
||||
assert(data != nullptr);
|
||||
if (data) ZeroMemory(data, length);
|
||||
return data;
|
||||
}
|
||||
|
||||
void* Memory::Allocate(size_t length)
|
||||
{
|
||||
void* data = calloc(length, 1);
|
||||
assert(data != nullptr);
|
||||
return data;
|
||||
}
|
||||
|
||||
char* Memory::DuplicateString(const std::string& string)
|
||||
{
|
||||
char* newString = Memory::AllocateArray<char>(string.size() + 1);
|
||||
std::memcpy(newString, string.data(), string.size());
|
||||
return newString;
|
||||
}
|
||||
|
||||
void Memory::Free(void* data)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
free(data);
|
||||
}
|
||||
}
|
||||
|
||||
void Memory::Free(const void* data)
|
||||
{
|
||||
Memory::Free(const_cast<void*>(data));
|
||||
}
|
||||
|
||||
void Memory::FreeAlign(void* data)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
_aligned_free(data);
|
||||
}
|
||||
}
|
||||
|
||||
void Memory::FreeAlign(const void* data)
|
||||
{
|
||||
Memory::FreeAlign(const_cast<void*>(data));
|
||||
}
|
||||
|
||||
// Complementary function for memset, which checks if memory is filled with a char
|
||||
bool Memory::IsSet(void* mem, char chr, size_t length)
|
||||
{
|
||||
char* memArr = reinterpret_cast<char*>(mem);
|
||||
|
||||
for (size_t i = 0; i < length; ++i)
|
||||
{
|
||||
if (memArr[i] != chr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Memory::IsBadReadPtr(const void* ptr)
|
||||
{
|
||||
MEMORY_BASIC_INFORMATION mbi = { nullptr };
|
||||
if (VirtualQuery(ptr, &mbi, sizeof(mbi)))
|
||||
{
|
||||
DWORD mask = (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY);
|
||||
bool b = !(mbi.Protect & mask);
|
||||
// check the page is not a guard page
|
||||
if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) b = true;
|
||||
|
||||
return b;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Memory::IsBadCodePtr(const void* ptr)
|
||||
{
|
||||
MEMORY_BASIC_INFORMATION mbi = { nullptr };
|
||||
if (VirtualQuery(ptr, &mbi, sizeof(mbi)))
|
||||
{
|
||||
DWORD mask = (PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY);
|
||||
bool b = !(mbi.Protect & mask);
|
||||
// check the page is not a guard page
|
||||
if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) b = true;
|
||||
|
||||
return b;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Utils::Memory::Allocator* Memory::GetAllocator()
|
||||
{
|
||||
return &Memory::MemAllocator;
|
||||
}
|
||||
}
|
163
src/Utils/Memory.hpp
Normal file
163
src/Utils/Memory.hpp
Normal file
@ -0,0 +1,163 @@
|
||||
#pragma once
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
class Memory
|
||||
{
|
||||
public:
|
||||
class Allocator
|
||||
{
|
||||
public:
|
||||
typedef void(*FreeCallback)(void*);
|
||||
|
||||
Allocator()
|
||||
{
|
||||
this->pool.clear();
|
||||
this->refMemory.clear();
|
||||
}
|
||||
~Allocator()
|
||||
{
|
||||
this->clear();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
std::lock_guard<std::mutex> _(this->mutex);
|
||||
|
||||
for (auto i = this->refMemory.begin(); i != this->refMemory.end(); ++i)
|
||||
{
|
||||
if (i->first && i->second)
|
||||
{
|
||||
i->second(i->first);
|
||||
}
|
||||
}
|
||||
|
||||
this->refMemory.clear();
|
||||
|
||||
for (auto& data : this->pool)
|
||||
{
|
||||
Memory::Free(data);
|
||||
}
|
||||
|
||||
this->pool.clear();
|
||||
}
|
||||
|
||||
void free(void* data)
|
||||
{
|
||||
std::lock_guard<std::mutex> _(this->mutex);
|
||||
|
||||
auto i = this->refMemory.find(data);
|
||||
if (i != this->refMemory.end())
|
||||
{
|
||||
i->second(i->first);
|
||||
this->refMemory.erase(i);
|
||||
}
|
||||
|
||||
auto j = std::find(this->pool.begin(), this->pool.end(), data);
|
||||
if (j != this->pool.end())
|
||||
{
|
||||
Memory::Free(data);
|
||||
this->pool.erase(j);
|
||||
}
|
||||
}
|
||||
|
||||
void free(const void* data)
|
||||
{
|
||||
this->free(const_cast<void*>(data));
|
||||
}
|
||||
|
||||
void reference(void* memory, FreeCallback callback)
|
||||
{
|
||||
std::lock_guard<std::mutex> _(this->mutex);
|
||||
|
||||
this->refMemory[memory] = callback;
|
||||
}
|
||||
|
||||
void* allocate(size_t length)
|
||||
{
|
||||
std::lock_guard<std::mutex> _(this->mutex);
|
||||
|
||||
void* data = Memory::Allocate(length);
|
||||
this->pool.push_back(data);
|
||||
return data;
|
||||
}
|
||||
template <typename T> inline T* allocate()
|
||||
{
|
||||
return this->allocateArray<T>(1);
|
||||
}
|
||||
template <typename T> inline T* allocateArray(size_t count = 1)
|
||||
{
|
||||
return static_cast<T*>(this->allocate(count * sizeof(T)));
|
||||
}
|
||||
|
||||
bool empty()
|
||||
{
|
||||
return (this->pool.empty() && this->refMemory.empty());
|
||||
}
|
||||
|
||||
char* duplicateString(const std::string& string)
|
||||
{
|
||||
std::lock_guard<std::mutex> _(this->mutex);
|
||||
|
||||
char* data = Memory::DuplicateString(string);
|
||||
this->pool.push_back(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
bool isPointerMapped(void* ptr)
|
||||
{
|
||||
return this->ptrMap.find(ptr) != this->ptrMap.end();
|
||||
}
|
||||
|
||||
template <typename T> T* getPointer(void* oldPtr)
|
||||
{
|
||||
if (this->isPointerMapped(oldPtr))
|
||||
{
|
||||
return reinterpret_cast<T*>(this->ptrMap[oldPtr]);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void mapPointer(void* oldPtr, void* newPtr)
|
||||
{
|
||||
this->ptrMap[oldPtr] = newPtr;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex mutex;
|
||||
std::vector<void*> pool;
|
||||
std::unordered_map<void*, void*> ptrMap;
|
||||
std::unordered_map<void*, FreeCallback> refMemory;
|
||||
};
|
||||
|
||||
static void* AllocateAlign(size_t length, size_t alignment);
|
||||
static void* Allocate(size_t length);
|
||||
template <typename T> static inline T* Allocate()
|
||||
{
|
||||
return AllocateArray<T>(1);
|
||||
}
|
||||
template <typename T> static inline T* AllocateArray(size_t count = 1)
|
||||
{
|
||||
return static_cast<T*>(Allocate(count * sizeof(T)));
|
||||
}
|
||||
|
||||
static char* DuplicateString(const std::string& string);
|
||||
|
||||
static void Free(void* data);
|
||||
static void Free(const void* data);
|
||||
|
||||
static void FreeAlign(void* data);
|
||||
static void FreeAlign(const void* data);
|
||||
|
||||
static bool IsSet(void* mem, char chr, size_t length);
|
||||
|
||||
static bool IsBadReadPtr(const void* ptr);
|
||||
static bool IsBadCodePtr(const void* ptr);
|
||||
|
||||
static Utils::Memory::Allocator* GetAllocator();
|
||||
|
||||
private:
|
||||
static Utils::Memory::Allocator MemAllocator;
|
||||
};
|
||||
}
|
208
src/Utils/String.cpp
Normal file
208
src/Utils/String.cpp
Normal file
@ -0,0 +1,208 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
namespace String
|
||||
{
|
||||
const char *VA(const char *fmt, ...)
|
||||
{
|
||||
static VAProvider<4, 100> globalProvider;
|
||||
static thread_local VAProvider<8, 256> provider;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
|
||||
const char* result;
|
||||
if (Components::Loader::IsUninitializing()) result = globalProvider.get(fmt, ap);
|
||||
else result = provider.get(fmt, ap);
|
||||
|
||||
va_end(ap);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string ToLower(std::string input)
|
||||
{
|
||||
std::transform(input.begin(), input.end(), input.begin(), ::tolower);
|
||||
return input;
|
||||
}
|
||||
|
||||
std::string ToUpper(std::string input)
|
||||
{
|
||||
std::transform(input.begin(), input.end(), input.begin(), ::toupper);
|
||||
return input;
|
||||
}
|
||||
|
||||
std::string DumpHex(const std::string& data, const std::string& separator)
|
||||
{
|
||||
std::string result;
|
||||
|
||||
for (unsigned int i = 0; i < data.size(); ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
result.append(separator);
|
||||
}
|
||||
|
||||
result.append(Utils::String::VA("%02X", data[i] & 0xFF));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string XOR(std::string str, char value)
|
||||
{
|
||||
for (unsigned int i = 0; i < str.size(); ++i)
|
||||
{
|
||||
str[i] ^= value;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
std::vector<std::string> Explode(const std::string& str, char delim)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
std::istringstream iss(str);
|
||||
|
||||
for (std::string token; std::getline(iss, token, delim);)
|
||||
{
|
||||
std::string _entry = std::move(token);
|
||||
|
||||
// Remove trailing 0x0 bytes
|
||||
while (_entry.size() && !_entry.back())
|
||||
{
|
||||
_entry = _entry.substr(0, _entry.size() - 1);
|
||||
}
|
||||
|
||||
result.push_back(_entry);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Replace(std::string &string, const std::string& find, const std::string& replace)
|
||||
{
|
||||
size_t nPos = 0;
|
||||
|
||||
while ((nPos = string.find(find, nPos)) != std::string::npos)
|
||||
{
|
||||
string = string.replace(nPos, find.length(), replace);
|
||||
nPos += replace.length();
|
||||
}
|
||||
}
|
||||
|
||||
bool StartsWith(const std::string& haystack, const std::string& needle)
|
||||
{
|
||||
return (haystack.size() >= needle.size() && haystack.substr(0, needle.size()) == needle);
|
||||
}
|
||||
|
||||
bool EndsWith(const std::string& haystack, const std::string& needle)
|
||||
{
|
||||
return (haystack.size() >= needle.size() && haystack.substr(haystack.size() - needle.size()) == needle);
|
||||
}
|
||||
|
||||
int IsSpace(int c)
|
||||
{
|
||||
if (c < -1) return 0;
|
||||
return _isspace_l(c, nullptr);
|
||||
}
|
||||
|
||||
// trim from start
|
||||
std::string <rim(std::string &s)
|
||||
{
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int val)
|
||||
{
|
||||
return !IsSpace(val);
|
||||
}));
|
||||
return s;
|
||||
}
|
||||
|
||||
// trim from end
|
||||
std::string &RTrim(std::string &s)
|
||||
{
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), [](int val)
|
||||
{
|
||||
return !IsSpace(val);
|
||||
}).base(), s.end());
|
||||
return s;
|
||||
}
|
||||
|
||||
// trim from both ends
|
||||
std::string &Trim(std::string &s)
|
||||
{
|
||||
return LTrim(RTrim(s));
|
||||
}
|
||||
|
||||
std::string FormatTimeSpan(int milliseconds)
|
||||
{
|
||||
int secondsTotal = milliseconds / 1000;
|
||||
int seconds = secondsTotal % 60;
|
||||
int minutesTotal = secondsTotal / 60;
|
||||
int minutes = minutesTotal % 60;
|
||||
int hoursTotal = minutesTotal / 60;
|
||||
|
||||
return Utils::String::VA("%02d:%02d:%02d", hoursTotal, minutes, seconds);
|
||||
}
|
||||
|
||||
std::string FormatBandwidth(size_t bytes, int milliseconds)
|
||||
{
|
||||
static const char* sizes[] =
|
||||
{
|
||||
"B",
|
||||
"KB",
|
||||
"MB",
|
||||
"GB",
|
||||
"TB"
|
||||
};
|
||||
|
||||
if (!milliseconds) return "0.00 B/s";
|
||||
|
||||
double bytesPerSecond = (1000.0 / milliseconds) * bytes;
|
||||
|
||||
int i;
|
||||
for (i = 0; bytesPerSecond > 1000 && i < ARRAYSIZE(sizes); ++i) // 1024 or 1000?
|
||||
{
|
||||
bytesPerSecond /= 1000;
|
||||
}
|
||||
|
||||
return Utils::String::VA("%.2f %s/s", static_cast<float>(bytesPerSecond), sizes[i]);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_BASE64
|
||||
// Encodes a given string in Base64
|
||||
std::string EncodeBase64(const char* input, const unsigned long inputSize)
|
||||
{
|
||||
unsigned long outlen = long(inputSize + (inputSize / 3.0) + 16);
|
||||
unsigned char* outbuf = new unsigned char[outlen]; //Reserve output memory
|
||||
base64_encode(reinterpret_cast<unsigned char*>(const_cast<char*>(input)), inputSize, outbuf, &outlen);
|
||||
std::string ret(reinterpret_cast<char*>(outbuf), outlen);
|
||||
delete[] outbuf;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Encodes a given string in Base64
|
||||
std::string EncodeBase64(const std::string& input)
|
||||
{
|
||||
return EncodeBase64(input.data(), input.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_BASE128
|
||||
// Encodes a given string in Base128
|
||||
std::string EncodeBase128(const std::string& input)
|
||||
{
|
||||
base128 encoder;
|
||||
|
||||
void* inbuffer = const_cast<char*>(input.data());
|
||||
char* buffer = encoder.encode(inbuffer, input.size());
|
||||
/*
|
||||
Interesting to see that the buffer returned by the encoder is not a standalone string copy
|
||||
but instead is a pointer to the internal "encoded" field of the encoder. So if you deinitialize
|
||||
the encoder that string will probably become garbage.
|
||||
*/
|
||||
std::string retval(buffer);
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
100
src/Utils/String.hpp
Normal file
100
src/Utils/String.hpp
Normal file
@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
namespace String
|
||||
{
|
||||
template <size_t Buffers, size_t MinBufferSize>
|
||||
class VAProvider
|
||||
{
|
||||
public:
|
||||
static_assert(Buffers != 0 && MinBufferSize != 0, "Buffers and MinBufferSize mustn't be 0");
|
||||
|
||||
VAProvider() : currentBuffer(0) {}
|
||||
~VAProvider() {}
|
||||
|
||||
const char* get(const char* format, va_list ap)
|
||||
{
|
||||
++this->currentBuffer %= ARRAYSIZE(this->stringPool);
|
||||
auto entry = &this->stringPool[this->currentBuffer];
|
||||
|
||||
if (!entry->size || !entry->buffer)
|
||||
{
|
||||
throw std::runtime_error("String pool not initialized");
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
int res = vsnprintf_s(entry->buffer, entry->size, _TRUNCATE, format, ap);
|
||||
if (res > 0) break; // Success
|
||||
if (res == 0) return ""; // Error
|
||||
|
||||
entry->doubleSize();
|
||||
}
|
||||
|
||||
return entry->buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
class Entry
|
||||
{
|
||||
public:
|
||||
Entry(size_t _size = MinBufferSize) : size(_size), buffer(nullptr)
|
||||
{
|
||||
if (this->size < MinBufferSize) this->size = MinBufferSize;
|
||||
this->allocate();
|
||||
}
|
||||
|
||||
~Entry()
|
||||
{
|
||||
if (this->buffer) Utils::Memory::GetAllocator()->free(this->buffer);
|
||||
this->size = 0;
|
||||
this->buffer = nullptr;
|
||||
}
|
||||
|
||||
void allocate()
|
||||
{
|
||||
if (this->buffer) Utils::Memory::GetAllocator()->free(this->buffer);
|
||||
this->buffer = Utils::Memory::GetAllocator()->allocateArray<char>(this->size + 1);
|
||||
}
|
||||
|
||||
void doubleSize()
|
||||
{
|
||||
this->size *= 2;
|
||||
this->allocate();
|
||||
}
|
||||
|
||||
size_t size;
|
||||
char* buffer;
|
||||
};
|
||||
|
||||
size_t currentBuffer;
|
||||
Entry stringPool[Buffers];
|
||||
};
|
||||
|
||||
const char *VA(const char *fmt, ...);
|
||||
|
||||
int IsSpace(int c);
|
||||
std::string ToLower(std::string input);
|
||||
std::string ToUpper(std::string input);
|
||||
bool EndsWith(const std::string& haystack, const std::string& needle);
|
||||
std::vector<std::string> Explode(const std::string& str, char delim);
|
||||
void Replace(std::string &string, const std::string& find, const std::string& replace);
|
||||
bool StartsWith(const std::string& haystack, const std::string& needle);
|
||||
std::string <rim(std::string &s);
|
||||
std::string &RTrim(std::string &s);
|
||||
std::string &Trim(std::string &s);
|
||||
|
||||
std::string FormatTimeSpan(int milliseconds);
|
||||
std::string FormatBandwidth(size_t bytes, int milliseconds);
|
||||
|
||||
std::string DumpHex(const std::string& data, const std::string& separator = " ");
|
||||
|
||||
std::string XOR(const std::string str, char value);
|
||||
|
||||
std::string EncodeBase64(const char* input, const unsigned long inputSize);
|
||||
std::string EncodeBase64(const std::string& input);
|
||||
|
||||
std::string EncodeBase128(const std::string& input);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user