1
0
mirror of https://github.com/Mauler125/r5sdk.git synced 2025-02-09 19:15:03 +01:00

Alot of changes read description

* Ban list added
* Playlist parser added
* Kick functions added
* Connect uses encryption keys now
* Fixed restricted server commands
This commit is contained in:
IcePixelx 2021-09-08 00:32:03 +02:00
parent c89c7c9c6b
commit 2da6d71fb3
25 changed files with 1119 additions and 164 deletions

@ -26,20 +26,27 @@ public:
enum class EHostStatus {
NotHosting,
WaitingForStateChange,
Hosting,
ConnectedToSomeoneElse
Hosting
} HostingStatus = EHostStatus::NotHosting;
enum class EServerVisibility {
Offline,
Hidden,
Public
} ServerVisibility = EServerVisibility::Offline;
////////////////////
// Server Browser //
////////////////////
R5Net::Client* r5net;
R5Net::Client* GetR5Net() { return r5net; }
std::vector<ServerListing> ServerList;
ImGuiTextFilter ServerBrowserFilter;
char ServerConnStringBuffer[256] = { 0 };
char ServerEncKeyBuffer[30] = { 0 };
std::string ServerListMessage = std::string();
////////////////////
@ -56,16 +63,14 @@ public:
std::string HostToken = "";
ImVec4 HostRequestMessageColor = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
bool StartAsDedi = false;
bool BroadCastServer = false;
bool OverridePlaylist = false;
////////////////////
// Private Server //
////////////////////
std::string PrivateServerToken = "";
std::string PrivateServerPassword = "";
std::string PrivateServerRequestMessage = "";
ImVec4 PrivateServerMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f);
std::string HiddenServerToken = "";
std::string HiddenServerRequestMessage = "";
ImVec4 HiddenServerMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f);
/* Texture */
ID3D11ShaderResourceView* ApexLockIcon = nullptr;
@ -144,14 +149,20 @@ public:
void CompMenu();
void ServerBrowserSection();
void SettingsSection();
void HiddenServersModal();
void HostServerSection();
void Draw(const char* title);
void UpdateHostingStatus();
void ProcessCommand(const char* command_line);
void ExecCommand(const char* command_line);
void ConnectToServer(const std::string &ip, const std::string &port);
void ConnectToServer(const std::string &connString);
void RegenerateEncryptionKey();
void ChangeEncryptionKeyTo(const std::string str);
void ConnectToServer(const std::string ip, const std::string port, const std::string encKey);
void ConnectToServer(const std::string connString, const std::string encKey);
};
extern CCompanion* g_ServerBrowser;
extern CCompanion* g_ServerBrowser;
extern bool g_CheckCompBanDB;

@ -417,4 +417,6 @@ enum FileWarningLevel_t
#define FCVAR_SERVER_CAN_EXECUTE (1<<28)// the server is allowed to execute this command on clients via ClientCommand/NET_StringCmd/CBaseClientState::ProcessStringCmd.
#define FCVAR_SERVER_CANNOT_QUERY (1<<29)// If this is set, then the server is not allowed to query this cvar's value (via IServerPluginHelpers::StartQueryCvarValue).
#define FCVAR_CLIENTCMD_CAN_EXECUTE (1<<30) // IVEngineClient::ClientCmd is allowed to execute this command.
#define FCVAR_CLIENTCMD_CAN_EXECUTE (1<<30) // IVEngineClient::ClientCmd is allowed to execute this command.
#define MAX_PLAYERS 128 // Max R5 players.

@ -1,5 +1,6 @@
#pragma once
#include "patterns.h"
#include "banlist.h"
/////////////////////////////////////////////////////////////////////////////
// Classes and Structs
@ -238,8 +239,8 @@ struct QAngle // Implement the proper class of this at some point.
class CHostState
{
public:
__int32 m_iCurrentState; //0x0000
__int32 m_iNextState; //0x0004
int m_iCurrentState; //0x0000
int m_iNextState; //0x0004
Vector3 m_vecLocation; //0x0008
QAngle m_angLocation; //0x0014
char m_levelName[64]; //0x0020
@ -265,6 +266,98 @@ public:
}
};
class CClient
{
public:
inline CClient* GetClientInstance(int index)
{
return (CClient*)(std::uintptr_t)(0x16073B200 + (index * (std::uintptr_t)0x4A4C0));
}
void*& GetNetChan()
{
return m_nNetChannel;
}
private:
char pad_0000[16]; //0x0000
public:
int m_iUserID; //0x0010
private:
char pad_0014[908]; //0x0014
public:
void* m_nNetChannel; //0x03A0
private:
char pad_03A8[8]; //0x03A8
public:
int m_iSignonstate; //0x03B0
private:
char pad_03B4[4]; //0x03B4
public:
std::int64_t m_iOriginID; //0x03B8
private:
char pad_03C0[303360]; //0x03C0
};
class CCommand
{
private:
enum
{
COMMAND_MAX_ARGC = 64,
COMMAND_MAX_LENGTH = 512,
};
public:
CCommand() = delete;
inline int MaxCommandLength()
{
return COMMAND_MAX_LENGTH - 1;
}
inline int64_t ArgC() const
{
return m_nArgc;
}
inline const char** ArgV() const
{
return m_nArgc ? (const char**)m_ppArgv : NULL;
}
inline const char* ArgS() const
{
return m_nArgv0Size ? &m_pArgSBuffer[m_nArgv0Size] : "";
}
inline const char* GetCommandString() const
{
return m_nArgc ? m_pArgSBuffer : "";
}
inline const char* Arg(int nIndex) const
{
// FIXME: Many command handlers appear to not be particularly careful
// about checking for valid argc range. For now, we're going to
// do the extra check and return an empty string if it's out of range
if (nIndex < 0 || nIndex >= m_nArgc)
return "";
return m_ppArgv[nIndex];
}
inline const char* operator[](int nIndex) const
{
return Arg(nIndex);
}
private:
std::int64_t m_nArgc;
std::int64_t m_nArgv0Size;
char m_pArgSBuffer[COMMAND_MAX_LENGTH];
char m_pArgvBuffer[COMMAND_MAX_LENGTH];
const char* m_ppArgv[COMMAND_MAX_ARGC];
};
class ConCommandBase
{
public:
@ -368,16 +461,27 @@ struct Interface
namespace GameGlobals
{
// Class Instances
extern CHostState* HostState;
extern CInputSystem* InputSystem;
extern CCVar* Cvar;
extern KeyValues** PlaylistKeyValues;
extern CKeyValuesSystem* KeyValuesSystem;
extern std::vector<std::string> allPlaylists;
extern CClient* Client;
extern BanList* BanSystem;
// Var
ConVar* CreateCustomConVar(const char* name, const char* defaultValue, int flags, const char* helpString, bool bMin, float fMin, bool bMax, float fMax, void* callback, void* unk);
void* CreateCustomConCommand(const char* name, const char* helpString, int flags, void* callback, void* callbackAfterExecution);
// Init
void InitGameGlobals();
void InitConVars();
void InitAllCommandVariations();
void InitPlaylist();
extern std::vector<std::string> allPlaylists;
extern bool IsInitialized;
// Utility
void DisconnectClient(CClient* client, const char* reason, unsigned __int8 unk1, char unk2);
}

@ -27,12 +27,12 @@ public:
std::filesystem::path path = std::filesystem::current_path() /= "gui.config"; // Get current path + gui.config
nlohmann::json in;
std::ifstream config_file(path, std::ios::in); // Parse config file.
std::ifstream configFile(path, std::ios::in); // Parse config file.
if (config_file.good() && config_file) // Check if it parsed.
if (configFile.good() && configFile) // Check if it parsed.
{
config_file >> in;
config_file.close();
configFile >> in;
configFile.close();
if (!in.is_null())
{
@ -63,11 +63,11 @@ public:
out["config"]["CCompanion"]["bind2"] = CCompanionConfig.bind2;
std::filesystem::path path = std::filesystem::current_path() /= "gui.config"; // Get current path + gui.config
std::ofstream out_file(path, std::ios::out | std::ios::trunc); // Write config file..
std::ofstream outFile(path, std::ios::out | std::ios::trunc); // Write config file..
out_file << out.dump(4); // Dump it into config file..
outFile << out.dump(4); // Dump it into config file..
out_file.close(); // Close the file handle.
outFile.close(); // Close the file handle.
};
};

@ -36,6 +36,13 @@ namespace Hooks
extern SQVM_LoadScriptFn originalSQVM_LoadScript;
#pragma endregion
#pragma region CServer
void* ConnectClient(void* thisptr, void* packet);
using ConnectClientFn = void* (*)(void*, void*);
extern ConnectClientFn originalConnectClient;
#pragma endregion
#pragma region CVEngineServer
bool IsPersistenceDataAvailable(__int64 thisptr, int client);
@ -47,7 +54,7 @@ namespace Hooks
bool NET_ReceiveDatagram(int sock, void* inpacket, bool raw);
unsigned int NET_SendDatagram(SOCKET s, const char* buf, int len, int flags);
void NET_PrintFunc(const char* fmt, ...);
void NetChanShutdown(void* rcx, const char* reason, unsigned __int8 unk1, char unk2);
void NetChan_Shutdown(void* rcx, const char* reason, unsigned __int8 unk1, char unk2);
using NET_PrintFuncFn = void(*)(const char* fmt, ...);
extern NET_PrintFuncFn originalNET_PrintFunc;
@ -58,7 +65,7 @@ namespace Hooks
using NET_SendDatagramFn = unsigned int(*)(SOCKET, const char*, int, int);
extern NET_SendDatagramFn originalNET_SendDatagram;
using NetChan_ShutDown = void(*)(void*, const char*, unsigned __int8, char);
extern NetChan_ShutDown originalNetChanShutDown;
extern NetChan_ShutDown originalNetChan_ShutDown;
#pragma endregion
#pragma region ConVar
@ -70,9 +77,6 @@ namespace Hooks
using ConCommand_IsFlagSetFn = bool(*)(ConCommandBase*, int);
extern ConCommand_IsFlagSetFn originalConCommand_IsFlagSet;
using Map_CallbackFn = void(*)(void*);
extern Map_CallbackFn originalMap_Callback;
#pragma endregion
#pragma region WinAPI

@ -29,6 +29,8 @@ namespace
#pragma region NetChannel
/*0x14030D000*/
MemoryAddress CServer_Auth = r5_op.PatternSearch("40 55 57 41 55 41 57 48 8D AC 24 ? ? ? ?");
/*0x1413336F0*/
MemoryAddress NetChan_EncKey_DefaultAssign = r5_op.PatternSearch("E8 ? ? ? ? 48 8D 1D ? ? ? ? 4C 39 3D ? ? ? ?");
#pragma endregion
#pragma region FairFight
@ -50,6 +52,7 @@ namespace
PRINT_ADDRESS("dst004", dst004.GetPtr());
PRINT_ADDRESS("Host_NewGame", Host_NewGame.GetPtr());
PRINT_ADDRESS("CServer_Auth", CServer_Auth.GetPtr());
PRINT_ADDRESS("NetChan_EncKey_DefaultAssign", NetChan_EncKey_DefaultAssign.GetPtr());
PRINT_ADDRESS("FairFight_Init", FairFight_Init.GetPtr());
PRINT_ADDRESS("Squirrel_CompileError", Squirrel_CompileError.GetPtr());
std::cout << "+--------------------------------------------------------+" << std::endl;

@ -50,7 +50,24 @@ namespace
FUNC_AT_ADDRESS(addr_NET_SendDatagram, int(*)(SOCKET, const char*, int, int), r5_patterns.PatternSearch("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 41 56 41 57 48 81 EC ? 05 ? ?").GetPtr());
/*0x14025F190*/
FUNC_AT_ADDRESS(addr_NetChan_Shutdown, void(*)(void*, const char*, unsigned __int8, char), r5_patterns.StringSearch("Disconnect by server.\n").FindPatternSelf("E8 ? ? ? ? 4C 89 B3 ? ? ? ?", MemoryAddress::Direction::DOWN).FollowNearCall().GetPtr());
FUNC_AT_ADDRESS(addr_NetChan_Shutdown, void(*)(void*, const char*, unsigned __int8, char), r5_patterns.StringSearch("Disconnect by server.\n").FindPatternSelf("E8 ? ? ? ? 4C 89 B3 ? ? ? ?", MemoryAddress::Direction::DOWN).FollowNearCallSelf().GetPtr());
/*0x160686DC0*/
MemoryAddress addr_NetChan_EncKeyPtr = MemoryAddress(0x160686DC0);
char* addr_NetChan_EncKey = addr_NetChan_EncKeyPtr.Offset(4816).RCast<char*>();
/*0x140263E70*/
FUNC_AT_ADDRESS(addr_NetChan_SetEncKey, void(*)(uintptr_t, const char*), MemoryAddress(0x140263E70).GetPtr());
#pragma endregion
#pragma region CServer
/*0x140310230*/
FUNC_AT_ADDRESS(addr_CServer_RejectConnection, void(*)(void*, unsigned int, void*, const char*), r5_patterns.StringSearch("#CONNECTION_FAILED_RESERVATION_TIMEOUT").FindPatternSelf("E8", MemoryAddress::Direction::DOWN).FollowNearCallSelf().GetPtr());
/*0x14030D000*/
FUNC_AT_ADDRESS(addr_CServer_ConnectClient, void*(*)(void*, void*), r5_patterns.StringSearch("dedi.connect.fail.total:1|c\n").FindPatternSelf("E8", MemoryAddress::Direction::UP).FollowNearCallSelf().GetPtr());
#pragma endregion
#pragma region CHLClient
@ -58,6 +75,16 @@ namespace
FUNC_AT_ADDRESS(addr_CHLClient_FrameStageNotify, void(*)(void*, int), r5_patterns.PatternSearch("48 83 EC 28 89 15 ?? ?? ?? ??").GetPtr());
#pragma endregion
#pragma region CClient
/*0x140302FD0*/
FUNC_AT_ADDRESS(addr_CClient_Clear, void(*)(__int64), r5_patterns.StringSearch("Disconnect by server.\n").FindPatternSelf("40", MemoryAddress::Direction::UP).GetPtr());
#pragma endregion
#pragma region CClientState
/*0x1418223E4*/
FUNC_AT_ADDRESS(addr_m_bRestrictServerCommands, void*, r5_patterns.StringSearch("DevShotGenerator_Init()").FindPatternSelf("88 05", MemoryAddress::Direction::UP).ResolveRelativeAddressSelf(0x2).OffsetSelf(0x2).GetPtr());
#pragma endregion
#pragma region CVEngineServer
/*0x140315CF0*/
FUNC_AT_ADDRESS(addr_CVEngineServer_IsPersistenceDataAvailable, bool(*)(__int64, int), r5_patterns.PatternSearch("3B 15 ?? ?? ?? ?? 7D 33").GetPtr());
@ -80,7 +107,7 @@ namespace
FUNC_AT_ADDRESS(addr_LoadPlaylist, bool(*)(const char*), r5_patterns.PatternSearch("E8 ? ? ? ? 80 3D ? ? ? ? ? 74 0C").FollowNearCallSelf().GetPtr());
/*0x1671060C0*/
FUNC_AT_ADDRESS(addr_MapVPKCache, void*, r5_patterns.StringSearch("PrecacheMTVF").FindPatternSelf("48 8D 1D ? ? ? ? 4C", MemoryAddress::Direction::UP, 900).OffsetSelf(0x3).ResolveRelativeAddress().GetPtr());
FUNC_AT_ADDRESS(addr_MapVPKCache, void*, r5_patterns.StringSearch("PrecacheMTVF").FindPatternSelf("48 8D 1D ? ? ? ? 4C", MemoryAddress::Direction::UP, 900).OffsetSelf(0x3).ResolveRelativeAddressSelf().GetPtr());
/*0x140278C50*/
FUNC_AT_ADDRESS(addr_mp_gamemode_Callback, bool(*)(const char*), r5_patterns.StringSearch("Failed to load playlist data\n").FindPatternSelf("E8 ? ? ? ? B0 01", MemoryAddress::Direction::DOWN, 200).FollowNearCallSelf().GetPtr());
@ -108,6 +135,7 @@ namespace
PRINT_ADDRESS("NET_PrintFunc", addr_NET_PrintFunc);
PRINT_ADDRESS("NET_ReceiveDatagram", addr_NET_ReceiveDatagram);
PRINT_ADDRESS("NET_SendDatagram ", addr_NET_SendDatagram);
PRINT_ADDRESS("CClientState::m_bRestrictServerCommands", addr_m_bRestrictServerCommands);
PRINT_ADDRESS("INetChannel::Shutdown", addr_NetChan_Shutdown);
PRINT_ADDRESS("CHLClient::FrameStageNotify", addr_CHLClient_FrameStageNotify);
PRINT_ADDRESS("CVEngineServer::IsPersistenceDataAvailable", addr_CVEngineServer_IsPersistenceDataAvailable);

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
@ -148,7 +148,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<ModuleDefinitionFile>r5dev.def</ModuleDefinitionFile>
<AdditionalDependencies>Minhook.x64.lib;r5net.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Minhook.x64.lib;r5net.lib;bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PostBuildEvent>
<Command>del "$(SolutionDir)bin\$(Configuration)\r5dev.dll"
@ -189,7 +189,7 @@ if not EXIST $(SolutionDir)r5net\lib\$(Configuration)\r5net.lib (
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<ModuleDefinitionFile>r5dev.def</ModuleDefinitionFile>
<AdditionalDependencies>Minhook.x64.lib;r5net.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Minhook.x64.lib;r5net.lib;bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PostBuildEvent>
<Command>
@ -310,6 +310,7 @@ if not EXIST $(SolutionDir)r5net\lib\$(Configuration)\r5net.lib (
<ClInclude Include="..\external\spdlog\include\tweakme.h" />
<ClInclude Include="..\external\spdlog\include\version.h" />
<ClInclude Include="..\shared\include\address.h" />
<ClInclude Include="..\shared\include\banlist.h" />
<ClInclude Include="..\shared\include\httplib.h" />
<ClInclude Include="..\shared\include\json.hpp" />
<ClInclude Include="..\shared\include\utility.h" />
@ -379,6 +380,7 @@ if not EXIST $(SolutionDir)r5net\lib\$(Configuration)\r5net.lib (
</ClCompile>
<ClCompile Include="src\hooks\cbasefilesystem.cpp" />
<ClCompile Include="src\hooks\chlclient.cpp" />
<ClCompile Include="src\hooks\connectclient.cpp" />
<ClCompile Include="src\hooks\cvengineserver.cpp" />
<ClCompile Include="src\hooks\hooks.cpp" />
<ClCompile Include="src\hooks\iconvar.cpp" />

@ -103,6 +103,9 @@
<Filter Include="hooks\src\cbasefilesystem">
<UniqueIdentifier>{90ee1072-2d57-4d4e-aa1e-f19a81e6b27f}</UniqueIdentifier>
</Filter>
<Filter Include="hooks\src\cserver">
<UniqueIdentifier>{10edfee7-8c10-41de-b8f3-424826d2614a}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\dllmain.cpp">
@ -195,6 +198,9 @@
<ClCompile Include="src\hooks\loadplaylist.cpp">
<Filter>hooks\src\other</Filter>
</ClCompile>
<ClCompile Include="src\hooks\connectclient.cpp">
<Filter>hooks\src\cserver</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\external\imgui\include\imgui_impl_win32.h">
@ -564,6 +570,9 @@
<Filter>gui\include</Filter>
</ClInclude>
<ClInclude Include="resource.h" />
<ClInclude Include="..\shared\include\banlist.h">
<Filter>shared\include</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="r5dev.def">

@ -10,6 +10,7 @@
//#define OVERLAY_DEBUG
CCompanion* g_ServerBrowser = nullptr;
bool g_CheckCompBanDB = true;
std::map<std::string, std::string> mapArray =
{
@ -85,12 +86,24 @@ void CCompanion::UpdateHostingStatus()
}
case EHostStatus::Hosting:
{
if (!BroadCastServer) // Do we wanna broadcast server to the browser?
if (ServerVisibility == EServerVisibility::Offline) // Do we wanna broadcast server to the browser?
break;
if (*reinterpret_cast<std::int32_t*>(0x1656057E0) == NULL) // Check if script checksum is valid yet.
break;
switch (ServerVisibility) {
case EServerVisibility::Hidden:
MyServer.hidden = true;
break;
case EServerVisibility::Public:
MyServer.hidden = false;
break;
}
SendHostingPostRequest();
break;
}
@ -125,14 +138,17 @@ void CCompanion::SendHostingPostRequest()
{
HostToken = std::string();
bool result = r5net->PostServerHost(HostRequestMessage,HostToken,
ServerListing{ MyServer.name,
std::string(GameGlobals::HostState->m_levelName),
"",
GameGlobals::Cvar->FindVar("hostport")->m_pzsCurrentValue,
GameGlobals::Cvar->FindVar("mp_gamemode")->m_pzsCurrentValue,
MyServer.password,
std::to_string(*reinterpret_cast<std::int32_t*>(0x1656057E0)),
std::string()}
ServerListing{
MyServer.name,
std::string(GameGlobals::HostState->m_levelName),
"",
GameGlobals::Cvar->FindVar("hostport")->m_pzsCurrentValue,
GameGlobals::Cvar->FindVar("mp_gamemode")->m_pzsCurrentValue,
MyServer.hidden,
std::to_string(*reinterpret_cast<std::int32_t*>(0x1656057E0)),
std::string(),
addr_NetChan_EncKey
}
);
if (result)
@ -222,9 +238,9 @@ void CCompanion::ServerBrowserSection()
std::string selectButtonText = "Connect##";
selectButtonText += (server.name + server.ip + server.map);
if (ImGui::Button(selectButtonText.c_str()))
if (ImGui::Button(selectButtonText.c_str()))
{
ConnectToServer(server.ip, server.port);
ConnectToServer(server.ip, server.port, server.netchanEncryptionKey);
}
}
@ -235,28 +251,37 @@ void CCompanion::ServerBrowserSection()
ImGui::Separator();
ImGui::InputTextWithHint("##ServerBrowser_ServerConnString", "Enter IP address or \"localhost\"", ServerConnStringBuffer, IM_ARRAYSIZE(ServerConnStringBuffer));
ImGui::SameLine();
if (ImGui::Button("Connect##ServerBrowser_ConnectByIp", ImVec2(ImGui::GetWindowContentRegionWidth() * (1.f / 3.f / 2.f), 19)))
ImGui::PushItemWidth(ImGui::GetWindowContentRegionWidth() / 4);
{
//const char* replace = ""; // For history pos soon
std::stringstream cmd;
cmd << "connect " << ServerConnStringBuffer;
ConnectToServer(ServerConnStringBuffer);
//strcpy_s(ServerConnStringBuffer, sizeof(replace), replace); // For history pos soon
ImGui::InputTextWithHint("##ServerBrowser_ServerConnString", "Enter IP address or \"localhost\"", ServerConnStringBuffer, IM_ARRAYSIZE(ServerConnStringBuffer));
ImGui::SameLine(); ImGui::InputTextWithHint("##ServerBrowser_ServerEncKey", "Enter the encryption key", ServerEncKeyBuffer, IM_ARRAYSIZE(ServerEncKeyBuffer));
if (ImGui::SameLine(); ImGui::Button("Connect##ServerBrowser_ConnectByIp", ImVec2(ImGui::GetWindowContentRegionWidth() / 4, 18.5)))
{
ConnectToServer(ServerConnStringBuffer, ServerEncKeyBuffer);
}
ImGui::SameLine();
if (ImGui::Button("Private Servers##ServerBrowser_HiddenServersButton", ImVec2(ImGui::GetWindowContentRegionWidth() / 4, 18.5)))
{
ImGui::OpenPopup("Connect to Private Server##HiddenServersConnectModal");
}
HiddenServersModal();
// TODO: Still not in a seperate class...
}
ImGui::PopItemWidth();
ImGui::SameLine();
if (ImGui::Button("Private Servers##ServerBrowser_PrivateServersButton", ImVec2(ImGui::GetWindowContentRegionWidth() * (1.f / 3.f / 2.f), 19)))
{
ImGui::OpenPopup("Connect to Private Server##PrivateServersConnectModal");
}
}
void CCompanion::HiddenServersModal()
{
bool modalOpen = true;
if (ImGui::BeginPopupModal("Connect to Private Server##PrivateServersConnectModal", &modalOpen))
if (ImGui::BeginPopupModal("Connect to Private Server##HiddenServersConnectModal", &modalOpen))
{
// I *WILL* move this in a separate class
@ -322,7 +347,7 @@ void CCompanion::ServerBrowserSection()
}
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.00f, 0.00f, 0.00f, 0.00f)); // Override the style color for child bg.
ImGui::BeginChild("##PrivateServersConnectModal_IconParent", ImVec2(ApexLockIconWidth, ApexLockIconHeight));
ImGui::BeginChild("##HiddenServersConnectModal_IconParent", ImVec2(ApexLockIconWidth, ApexLockIconHeight));
{
ImGui::Image(ApexLockIcon, ImVec2(ApexLockIconWidth, ApexLockIconHeight)); // Display texture.
}
@ -334,38 +359,37 @@ void CCompanion::ServerBrowserSection()
ImGui::Text("Enter the following details to continue");
ImGui::PushItemWidth(ImGui::GetWindowContentRegionWidth()); // Override item width.
ImGui::InputTextWithHint("##PrivateServersConnectModal_TokenInput", "Token", &PrivateServerToken);
ImGui::InputTextWithHint("##PrivateServersConnectModal_PasswordInput", "Password", &PrivateServerPassword, ImGuiInputTextFlags_Password);
ImGui::InputTextWithHint("##HiddenServersConnectModal_TokenInput", "Token", &HiddenServerToken);
ImGui::PopItemWidth(); // Pop item width.
ImGui::Dummy(ImVec2(ImGui::GetWindowContentRegionWidth(), 19.f)); // Place a dummy, basically making space inserting a blank element.
ImGui::TextColored(PrivateServerMessageColor, PrivateServerRequestMessage.c_str());
ImGui::TextColored(HiddenServerMessageColor, HiddenServerRequestMessage.c_str());
ImGui::Separator();
if (ImGui::Button("Connect##PrivateServersConnectModal_ConnectButton", ImVec2(ImGui::GetWindowContentRegionWidth() / 2.f, 19)))
if (ImGui::Button("Connect##HiddenServersConnectModal_ConnectButton", ImVec2(ImGui::GetWindowContentRegionWidth() / 2, 24)))
{
PrivateServerRequestMessage = "";
HiddenServerRequestMessage = "";
ServerListing server;
bool result = r5net->GetServerByToken(server, PrivateServerRequestMessage, PrivateServerToken, PrivateServerPassword); // Send token connect request.
bool result = r5net->GetServerByToken(server, HiddenServerRequestMessage, HiddenServerToken); // Send token connect request.
if (!server.name.empty())
{
ConnectToServer(server.ip, server.port); // Connect to the server
PrivateServerRequestMessage = "Found Server: " + server.name;
PrivateServerMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f);
ConnectToServer(server.ip, server.port, server.netchanEncryptionKey); // Connect to the server
HiddenServerRequestMessage = "Found Server: " + server.name;
HiddenServerMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f);
ImGui::CloseCurrentPopup();
}
else
{
PrivateServerRequestMessage = "Error: " + PrivateServerRequestMessage;
PrivateServerMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
HiddenServerRequestMessage = "Error: " + HiddenServerRequestMessage;
HiddenServerMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
}
}
ImGui::SameLine();
if (ImGui::Button("Close##PrivateServersConnectModal_CloseButton", ImVec2(ImGui::GetWindowContentRegionWidth() / 2.f, 19)))
if (ImGui::Button("Close##HiddenServersConnectModal_CloseButton", ImVec2(ImGui::GetWindowContentRegionWidth() / 2, 24)))
{
ImGui::CloseCurrentPopup();
}
@ -380,8 +404,20 @@ void CCompanion::HostServerSection()
static std::string ServerMap = std::string();
ImGui::InputTextWithHint("##ServerHost_ServerName", "Server Name (Required)", &MyServer.name);
ImGui::InputTextWithHint("##ServerHost_ServerPassword", "Password (Optional)", &MyServer.password);
ImGui::Spacing();
if (ImGui::BeginCombo("Playlist##ServerHost_PlaylistBox", MyServer.playlist.c_str()))
{
for (auto& item : GameGlobals::allPlaylists)
{
if (ImGui::Selectable(item.c_str(), item == MyServer.playlist))
{
MyServer.playlist = item;
}
}
ImGui::EndCombo();
}
if (ImGui::BeginCombo("Map##ServerHost_MapListBox", MyServer.map.c_str()))
{
for (auto& item : MapsList)
@ -399,25 +435,28 @@ void CCompanion::HostServerSection()
}
ImGui::EndCombo();
}
if (ImGui::BeginCombo("Playlist##ServerHost_PlaylistBox", MyServer.playlist.c_str()))
{
for (auto& item : GameGlobals::allPlaylists)
{
if (ImGui::Selectable(item.c_str(), item == MyServer.playlist))
{
MyServer.playlist = item;
}
}
ImGui::EndCombo();
}
// ImGui::Checkbox("Start as Dedicated Server (HACK)##ServerHost_DediCheckbox", &StartAsDedi);
ImGui::Checkbox("Load Global Ban List##ServerHost_CheckCompBanDBCheckbox", &g_CheckCompBanDB);
ImGui::Spacing();
ImGui::Checkbox("Start as Dedicated Server (HACK)##ServerHost_DediCheckbox", &StartAsDedi);
ImGui::SameLine();
ImGui::Text("Server Visiblity");
if (ImGui::SameLine(); ImGui::RadioButton("Offline##ServerHost_ServerChoice1", ServerVisibility == EServerVisibility::Offline))
{
ServerVisibility = EServerVisibility::Offline;
}
if (ImGui::SameLine(); ImGui::RadioButton("Hidden##ServerHost_ServerChoice2", ServerVisibility == EServerVisibility::Hidden))
{
ServerVisibility = EServerVisibility::Hidden;
}
if (ImGui::SameLine(); ImGui::RadioButton("Public##ServerHost_ServerChoice2", ServerVisibility == EServerVisibility::Public))
{
ServerVisibility = EServerVisibility::Public;
}
ImGui::Checkbox("Broadcast Server to Server Browser", &BroadCastServer);
ImGui::Spacing();
ImGui::Separator();
if (!GameGlobals::HostState->m_bActiveGame)
@ -501,12 +540,23 @@ void CCompanion::HostServerSection()
void CCompanion::SettingsSection()
{
ImGui::InputTextWithHint("##MatchmakingServerString", "Matchmaking Server String", & MatchmakingServerStringBuffer);
// Matchmaking Server String
ImGui::InputTextWithHint("##MatchmakingServerString", "Matchmaking Server String", &MatchmakingServerStringBuffer);
if (ImGui::Button("Update Settings"))
{
if (r5net) delete r5net;
if (r5net)
{
delete r5net;
}
r5net = new R5Net::Client(MatchmakingServerStringBuffer);
}
// Encryption Key
if (ImGui::Button("Regenerate Encryption Key##SettingsSection_RegenEncKey"))
{
RegenerateEncryptionKey();
}
ImGui::InputText("Encryption Key##SettingsSection_EncKey", addr_NetChan_EncKey, ImGuiInputTextFlags_ReadOnly);
}
void CCompanion::Draw(const char* title)
@ -559,20 +609,59 @@ void CCompanion::ExecCommand(const char* command_line)
addr_CommandExecute(NULL, command_line);
}
void CCompanion::ConnectToServer(const std::string& ip, const std::string& port)
void CCompanion::ConnectToServer(const std::string ip, const std::string port, const std::string encKey)
{
if (!encKey.empty())
{
ChangeEncryptionKeyTo(encKey);
}
std::stringstream cmd;
cmd << "connect " << ip << ":" << port;
ProcessCommand(cmd.str().c_str());
}
void CCompanion::ConnectToServer(const std::string& connString)
void CCompanion::ConnectToServer(const std::string connString, const std::string encKey)
{
if (!encKey.empty())
{
ChangeEncryptionKeyTo(encKey);
}
std::stringstream cmd;
cmd << "connect " << connString;
ProcessCommand(cmd.str().c_str());
}
void CCompanion::RegenerateEncryptionKey()
{
BCRYPT_ALG_HANDLE hAlgorithm;
if (BCryptOpenAlgorithmProvider(&hAlgorithm, L"RNG", 0, 0) < 0)
{
std::cerr << "Failed to open rng algorithm\n";
}
unsigned char pBuffer[0x10u];
if (BCryptGenRandom(hAlgorithm, pBuffer, 0x10u, 0) < 0)
{
std::cerr << "Failed to generate random data\n";
}
std::string fin;
for (int i = 0; i < 0x10u; i++)
{
fin += pBuffer[i];
}
fin = base64_encode(fin);
addr_NetChan_SetEncKey(addr_NetChan_EncKeyPtr.GetPtr(), fin.c_str());
}
void CCompanion::ChangeEncryptionKeyTo(const std::string str)
{
addr_NetChan_SetEncKey(addr_NetChan_EncKeyPtr.GetPtr(), str.c_str());
}
//#############################################################################
// ENTRYPOINT
//#############################################################################
@ -584,5 +673,6 @@ void DrawBrowser()
g_ServerBrowser = &browser;
return true;
} ();
browser.Draw("Companion");
}

@ -1,6 +1,7 @@
#include "pch.h"
#include "gameclasses.h"
#include "id3dx.h"
#include "cgameconsole.h"
// Need this for a re-factor later.
// Interface* interfaces = *reinterpret_cast<Interface**>(0x167F4FA48);
@ -16,60 +17,308 @@ namespace GameGlobals
CHostState* HostState = nullptr;
CInputSystem* InputSystem = nullptr;
CCVar* Cvar = nullptr;
CClient* Client = nullptr;
BanList* BanSystem = new BanList();
CKeyValuesSystem* KeyValuesSystem = nullptr;
KeyValues** PlaylistKeyValues = nullptr;
std::vector<std::string> allPlaylists = { "none" };
namespace CustomConVar
namespace CustomCommandVariations
{
void CGameConsole_Callback(void* cmd)
void CGameConsole_Callback(const CCommand& cmd)
{
std::string szValue = *(const char**)((std::uintptr_t)cmd + 0x18);
try
g_bShowConsole = !g_bShowConsole;
}
void CCompanion_Callback(const CCommand& cmd)
{
g_bShowBrowser = !g_bShowBrowser;
}
void Kick_Callback(CCommand* cmd)
{
std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4);
if (argSize < 2) // Do we atleast have 2 arguments?
return;
CCommand& cmdReference = *cmd; // Get reference.
const char* firstArg = cmdReference[1]; // Get first arg.
for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances.
{
int value = std::stoi(szValue);
switch (value)
{
case 0:
g_bShowConsole = false;
break;
case 1:
g_bShowConsole = true;
break;
default:
break;
}
}
catch (std::exception& e)
{
std::cout << " [+CGameConsole+] Please don't input a character that isn't a number into cgameconsole :(. Error: " << e.what() << std::endl;
CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance.
if (!client)
continue;
if (!client->GetNetChan()) // Netchan valid?
continue;
void* clientNamePtr = (void**)(((std::uintptr_t)client->GetNetChan()) + 0x1A8D); // Get client name from netchan.
std::string clientName((char*)clientNamePtr, 32); // Get full name.
if (clientName.empty()) // Empty name?
continue;
if (strcmp(firstArg, clientName.c_str()) != 0) // Our wanted name?
continue;
DisconnectClient(client, "Kicked from Server", 0, 1); // Disconnect client.
}
}
void CCompanion_Callback(void* cmd)
void KickID_Callback(CCommand* cmd)
{
std::string szValue = *(const char**)((std::uintptr_t)cmd + 0x18);
static auto HasOnlyDigits = [](const std::string& string)
{
for (const char& character : string)
{
if (std::isdigit(character) == 0)
return false;
}
return true;
};
std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4);
if (argSize < 2) // Do we atleast have 2 arguments?
return;
CCommand& cmdReference = *cmd; // Get reference.
std::string firstArg = cmdReference[1]; // Get first arg.
try
{
int value = std::stoi(szValue);
switch (value)
bool onlyDigits = HasOnlyDigits(firstArg); // Only has digits?
for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances.
{
case 0:
g_bShowBrowser = false;
break;
case 1:
g_bShowBrowser = true;
break;
default:
break;
CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance.
if (!client)
continue;
if (!client->GetNetChan()) // Netchan valid?
continue;
std::string finalIPAddress = "null"; // If this stays null they modified the packet somehow.
MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan.
if (ipAddressField)
{
std::stringstream ss;
ss << std::to_string(ipAddressField.GetValue<std::uint8_t>()) << "."
<< std::to_string(ipAddressField.Offset(0x1).GetValue<std::uint8_t>()) << "."
<< std::to_string(ipAddressField.Offset(0x2).GetValue<std::uint8_t>()) << "."
<< std::to_string(ipAddressField.Offset(0x3).GetValue<std::uint8_t>());
finalIPAddress = ss.str();
}
if (onlyDigits)
{
std::int64_t ID = static_cast<std::int64_t>(std::stoll(firstArg));
if (ID > MAX_PLAYERS) // Is it a possible originID?
{
std::int64_t originID = client->m_iOriginID;
if (originID != ID) // See if they match.
continue;
}
else // If its not try by userID.
{
std::int64_t clientID = static_cast<std::int64_t>(client->m_iUserID + 1); // Get UserID + 1.
if (clientID != ID) // See if they match.
continue;
}
DisconnectClient(client, "Kicked from Server", 0, 1); // Disconnect client.
}
else
{
if (firstArg.compare(finalIPAddress) != NULL) // Do the string equal?
continue;
DisconnectClient(client, "Kicked from Server", 0, 1); // Disconnect client.
}
}
}
catch (std::exception& e)
{
std::cout << " [+CCompanion+] Please don't input a character that isn't a number into ccompanion :(. Error: " << e.what() << std::endl;
std::cout << "Kick UID asked for a userID or originID :( You can get the userid with the 'status' command. Error: " << e.what() << std::endl;
g_GameConsole->AddLog("Kick UID asked for a userID or originID :( You can get the userid with the 'status' command. Error: %s", e.what());
return;
}
}
void Unban_Callback(CCommand* cmd)
{
static auto HasOnlyDigits = [](const std::string& string)
{
for (const char& character : string)
{
if (std::isdigit(character) == 0)
return false;
}
return true;
};
std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4);
if (argSize < 2) // Do we atleast have 2 arguments?
return;
CCommand& cmdReference = *cmd; // Get reference.
try
{
const char* firstArg = cmdReference[1];
if (HasOnlyDigits(firstArg)) // Check if we have an ip address or origin ID.
{
GameGlobals::BanSystem->DeleteEntry("noIP", std::stoll(firstArg)); // Delete ban entry.
GameGlobals::BanSystem->Save(); // Save modified vector to file.
}
else
{
GameGlobals::BanSystem->DeleteEntry(firstArg, 1); // Delete ban entry.
GameGlobals::BanSystem->Save(); // Save modified vector to file.
}
}
catch (std::exception& e)
{
std::cout << "Unban Error: " << e.what() << std::endl;
g_GameConsole->AddLog("Unban Error: %s", e.what());
return;
}
}
void ReloadBanList_Callback(CCommand* cmd)
{
GameGlobals::BanSystem->Load(); // Reload banlist.
}
void Ban_Callback(CCommand* cmd)
{
std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4);
if (argSize < 2) // Do we atleast have 2 arguments?
return;
CCommand& cmdReference = *cmd; // Get reference.
const char* firstArg = cmdReference[1]; // Get first arg.
for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances.
{
CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance.
if (!client)
continue;
if (!client->GetNetChan()) // Netchan valid?
continue;
void* clientNamePtr = (void**)(((std::uintptr_t)client->GetNetChan()) + 0x1A8D); // Get client name from netchan.
std::string clientName((char*)clientNamePtr, 32); // Get full name.
if (clientName.empty()) // Empty name?
continue;
if (strcmp(firstArg, clientName.c_str()) != 0) // Our wanted name?
continue;
std::string finalIPAddress = "null"; // If this stays null they modified the packet somehow.
MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan.
if (ipAddressField && ipAddressField.GetValue<int>() != 0x0)
{
std::stringstream ss;
ss << std::to_string(ipAddressField.GetValue<std::uint8_t>()) << "."
<< std::to_string(ipAddressField.Offset(0x1).GetValue<std::uint8_t>()) << "."
<< std::to_string(ipAddressField.Offset(0x2).GetValue<std::uint8_t>()) << "."
<< std::to_string(ipAddressField.Offset(0x3).GetValue<std::uint8_t>());
finalIPAddress = ss.str();
}
GameGlobals::BanSystem->AddEntry(finalIPAddress, client->m_iOriginID); // Add ban entry.
GameGlobals::BanSystem->Save(); // Save ban list.
DisconnectClient(client, "Banned from Server", 0, 1); // Disconnect client.
}
}
void BanID_Callback(CCommand* cmd)
{
static auto HasOnlyDigits = [](const std::string& string)
{
for (const char& character : string)
{
if (std::isdigit(character) == 0)
return false;
}
return true;
};
std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4);
if (argSize < 2) // Do we atleast have 2 arguments?
return;
CCommand& cmdReference = *cmd; // Get reference.
std::string firstArg = cmdReference[1];
try
{
bool onlyDigits = HasOnlyDigits(firstArg); // Only has digits?
for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances.
{
CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance.
if (!client)
continue;
if (!client->GetNetChan()) // Netchan valid?
continue;
std::string finalIPAddress = "null"; // If this stays null they modified the packet somehow.
MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan.
if (ipAddressField)
{
std::stringstream ss;
ss << std::to_string(ipAddressField.GetValue<std::uint8_t>()) << "."
<< std::to_string(ipAddressField.Offset(0x1).GetValue<std::uint8_t>()) << "."
<< std::to_string(ipAddressField.Offset(0x2).GetValue<std::uint8_t>()) << "."
<< std::to_string(ipAddressField.Offset(0x3).GetValue<std::uint8_t>());
finalIPAddress = ss.str();
}
if (onlyDigits)
{
std::int64_t ID = static_cast<std::int64_t>(std::stoll(firstArg));
if (ID > MAX_PLAYERS) // Is it a possible originID?
{
std::int64_t originID = client->m_iOriginID;
if (originID != ID) // See if they match.
continue;
}
else // If its not try by userID.
{
std::int64_t clientID = static_cast<std::int64_t>(client->m_iUserID + 1); // Get UserID + 1.
if (clientID != ID) // See if they match.
continue;
}
GameGlobals::BanSystem->AddEntry(finalIPAddress, client->m_iOriginID); // Add ban entry.
GameGlobals::BanSystem->Save(); // Save ban list.
DisconnectClient(client, "Banned from Server", 0, 1); // Disconnect client.
}
else
{
if (firstArg.compare(finalIPAddress) != NULL) // Do the string equal?
continue;
GameGlobals::BanSystem->AddEntry(finalIPAddress, client->m_iOriginID); // Add ban entry.
GameGlobals::BanSystem->Save(); // Save ban list.
DisconnectClient(client, "Banned from Server", 0, 1); // Disconnect client.
}
}
}
catch (std::exception& e)
{
std::cout << "Banid Error: " << e.what() << std::endl;
g_GameConsole->AddLog("Banid Error: %s", e.what());
return;
}
}
}
@ -110,9 +359,13 @@ namespace GameGlobals
Cvar = *reinterpret_cast<CCVar**>(0x14D40B348); // Get CCVar from memory.
KeyValuesSystem = reinterpret_cast<CKeyValuesSystem*>(0x141F105C0); // Get CKeyValuesSystem from memory.
PlaylistKeyValues = reinterpret_cast<KeyValues**>(0x16705B980); // Get the KeyValue for the playlist file.
Client = reinterpret_cast<CClient*>(0x16073B200);
NullHostNames(); // Null all hostnames.
InitConVars(); // Initialize our custom ConVars.
InitAllCommandVariations(); // Initialize our custom ConVars.
*(char*)addr_m_bRestrictServerCommands = true; // Restrict commands.
void* disconnect = Cvar->FindCommand("disconnect");
*(std::int32_t*)((std::uintptr_t)disconnect + 0x38) |= FCVAR_SERVER_CAN_EXECUTE; // Make sure server is not restricted to this.
std::thread t1(InitPlaylist); // Start thread to grab playlists.
t1.detach(); // Detach thread from current one.
@ -143,10 +396,49 @@ namespace GameGlobals
}
}
void InitConVars()
void InitAllCommandVariations()
{
ConVar* CGameConsoleConVar = CreateCustomConVar("cgameconsole", "0", 0, "Opens the R5 Reloaded Console", false, 0.f, false, 0.f, CustomConVar::CGameConsole_Callback, nullptr);
ConVar* CCompanionConVar = CreateCustomConVar("ccompanion", "0", 0, "Opens the R5 Reloaded Server Browser", false, 0.f, false, 0.f, CustomConVar::CCompanion_Callback, nullptr);
void* CGameConsoleConCommand = CreateCustomConCommand("cgameconsole", "Opens the R5 Reloaded Console.", 0, CustomCommandVariations::CGameConsole_Callback, nullptr);
void* CCompanionConCommand = CreateCustomConCommand("ccompanion", "Opens the R5 Reloaded Server Browser.", 0, CustomCommandVariations::CCompanion_Callback, nullptr);
void* KickConCommand = CreateCustomConCommand("kick", "Kick a client from the Server via name. | Usage: kick (name).", 0, CustomCommandVariations::Kick_Callback, nullptr);
void* KickIDConCommand = CreateCustomConCommand("kickid", "Kick a client from the Server via userID or originID | Usage: kickid (originID/userID)", 0, CustomCommandVariations::KickID_Callback, nullptr);
void* UnbanConCommand = CreateCustomConCommand("unban", "Unbans a client from the Server via IP or originID | Usage: unban (originID/ipAddress)", 0, CustomCommandVariations::Unban_Callback, nullptr);
void* ReloadBanListConCommand = CreateCustomConCommand("reloadbanlist", "Reloads the ban list from disk.", 0, CustomCommandVariations::ReloadBanList_Callback, nullptr);
void* BanConCommand = CreateCustomConCommand("ban", "Bans a client from the Server via name. | Usage: ban (name)", 0, CustomCommandVariations::Ban_Callback, nullptr);
void* BanIDConCommand = CreateCustomConCommand("banid", "Bans a client from the Server via originID, userID or IP | Usage: banid (originID/ipAddress/userID)", 0, CustomCommandVariations::BanID_Callback, nullptr);
}
void* CreateCustomConCommand(const char* name, const char* helpString, int flags, void* callback, void* callbackAfterExecution)
{
static MemoryAddress ConCommandVtable = MemoryAddress(0x14136BD70);
static MemoryAddress NullSub = MemoryAddress(0x1401B3280);
static MemoryAddress CallbackCompletion = MemoryAddress(0x1401E3990);
static MemoryAddress RegisterConCommand = MemoryAddress(0x14046F470);
void* command = reinterpret_cast<void*>(addr_MemAlloc_Wrapper(0x68)); // Allocate new memory with StdMemAlloc else we crash.
memset(command, 0, 0x68); // Set all to null.
std::uintptr_t commandPtr = reinterpret_cast<std::uintptr_t>(command); // To ptr.
*(void**)commandPtr = ConCommandVtable.RCast<void*>(); // 0x0 to ConCommand vtable.
*(const char**)(commandPtr + 0x18) = name; // 0x18 to ConCommand Name.
*(const char**)(commandPtr + 0x20) = helpString; // 0x20 to ConCommand help string.
*(std::int32_t*)(commandPtr + 0x38) = flags; // 0x38 to ConCommand Flags.
*(void**)(commandPtr + 0x40) = NullSub.RCast<void*>(); // 0x40 Nullsub since every concommand has it.
*(void**)(commandPtr + 0x50) = callback; // 0x50 has function callback.
*(std::int32_t*)(commandPtr + 0x60) = 2; // 0x60 Set to use callback and newcommand callback.
if (callbackAfterExecution) // Do we wanna have a callback after execution?
{
*(void**)(commandPtr + 0x58) = callbackAfterExecution; // 0x58 to our callback after execution.
}
else
{
*(void**)(commandPtr + 0x58) = CallbackCompletion.RCast<void*>(); // 0x58 nullsub.
}
RegisterConCommand.RCast<void(*)(void*)>()((void*)commandPtr); // Register command in ConVarAccessor.
return command;
}
ConVar* CreateCustomConVar(const char* name, const char* defaultValue, int flags, const char* helpString, bool bMin, float fMin, bool bMax, float fMax, void* callback, void* unk)
@ -160,20 +452,34 @@ namespace GameGlobals
std::uintptr_t cvarPtr = reinterpret_cast<std::uintptr_t>(allocatedConvar); // To ptr.
*(void**)(cvarPtr + 0x40) = ICvarVtable.RCast<void*>(); // 0x40 to ICvar table.
*(void**)cvarPtr = ConVarVtable.RCast<void*>(); // 0x0 to ConVar table.
*(void**)cvarPtr = ConVarVtable.RCast<void*>(); // 0x0 to ConVar vtable.
CreateConVar.RCast<void(*)(ConVar*, const char*, const char*, int, const char*, bool, float, bool, float, void*, void*)>() // Call to create ConVar.
(allocatedConvar, name, defaultValue, flags, helpString, bMin, fMin, bMax, fMax, callback, unk);
return allocatedConvar; // Return allocated ConVar.
}
void DisconnectClient(CClient* client, const char* reason, unsigned __int8 unk1, char unk2)
{
if (!client) // Client valid?
return;
if (std::strlen(reason) == NULL) // Is reason null?
return;
if (!client->GetNetChan())
return;
addr_NetChan_Shutdown(client->GetNetChan(), reason, unk1, unk2); // Shutdown netchan.
client->GetNetChan() = nullptr; // Null netchan.
MemoryAddress(0x140302FD0).RCast<void(*)(CClient*)>()(client); // Reset CClient instance for client.
}
}
#pragma region KeyValues
const char* KeyValues::GetName()
{
return GameGlobals::KeyValuesSystem->GetStringForSymbol(MAKE_3_BYTES_FROM_1_AND_2(m_iKeyNameCaseSensitive, m_iKeyNameCaseSensitive2));
}
#pragma endregion

@ -17,6 +17,34 @@ void __fastcall Hooks::FrameStageNotify(CHLClient* rcx, ClientFrameStage_t curSt
break;
}
case FRAME_NET_UPDATE_POSTDATAUPDATE_END:
{
if (GameGlobals::BanSystem->IsRefuseListValid())
{
for (int i = 0; i < GameGlobals::BanSystem->refuseList.size(); i++) // Loop through vector.
{
for (int c = 0; c < MAX_PLAYERS; c++) // Loop through all possible client instances.
{
CClient* client = GameGlobals::Client->GetClientInstance(c); // Get client instance.
if (!client)
continue;
if (!client->GetNetChan()) // Netchan valid?
continue;
int clientID = client->m_iUserID + 1; // Get UserID + 1.
if (clientID != GameGlobals::BanSystem->refuseList[i].second) // See if they match.
continue;
GameGlobals::DisconnectClient(client, GameGlobals::BanSystem->refuseList[i].first.c_str(), 0, 1);
GameGlobals::BanSystem->DeleteConnectionRefuse(clientID);
break;
}
}
}
PatchNetVarConVar();
break;
}
default:
break;
}

@ -0,0 +1,91 @@
#include "pch.h"
#include "hooks.h"
namespace Hooks
{
ConnectClientFn originalConnectClient = nullptr;;
}
void IsClientBanned(R5Net::Client* r5net, const std::string ip, std::int64_t orid)
{
std::string err = std::string();
bool compBanned = r5net && r5net->GetClientIsBanned(ip, orid, err);
if (compBanned)
{
while (compBanned)
{
std::this_thread::sleep_for(std::chrono::milliseconds(500));
for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances.
{
CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance.
if (!client) // Client instance valid?
continue;
if (!client->GetNetChan()) // Netchan valid?
continue;
std::int64_t originID = client->m_iOriginID; // Get originID.
if (originID != orid) // See if they match.
continue;
GameGlobals::BanSystem->AddConnectionRefuse(err, client->m_iUserID + 1); // Add to the vector.
compBanned = false;
break;
}
}
}
}
void* Hooks::ConnectClient(void* thisptr, void* packet)
{
if (!GameGlobals::BanSystem)
return originalConnectClient(thisptr, packet);
static int skipConnects = 0;
if (skipConnects != 2) // Skip first two connect attempts.
{
skipConnects++;
return originalConnectClient(thisptr, packet);
}
std::string finalIPAddress = "null";
MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)packet + 0x10));
if (ipAddressField && ipAddressField.GetValue<int>() != 0x0)
{
std::stringstream ss;
ss << std::to_string(ipAddressField.GetValue<std::uint8_t>()) << "."
<< std::to_string(ipAddressField.Offset(0x1).GetValue<std::uint8_t>()) << "."
<< std::to_string(ipAddressField.Offset(0x2).GetValue<std::uint8_t>()) << "."
<< std::to_string(ipAddressField.Offset(0x3).GetValue<std::uint8_t>());
finalIPAddress = ss.str();
}
R5Net::Client* r5net = g_ServerBrowser->GetR5Net();
const char* name = *(const char**)((std::uintptr_t)packet + 0x30);
std::int64_t originID = *(std::int64_t*)((std::uintptr_t)packet + 0x28);
g_GameConsole->AddLog("[CServer::ConnectClient] %s is trying to connect. OriginID: %lld", name, originID);
if (GameGlobals::BanSystem->IsBanListValid()) // Is the banlist vector valid?
{
if (GameGlobals::BanSystem->IsBanned(finalIPAddress, originID)) // Is the client trying to connect banned?
{
addr_CServer_RejectConnection(thisptr, *(unsigned int*)((std::uintptr_t)thisptr + 0xC), packet, "You have been banned from this Server."); // RejectConnection for the client.
g_GameConsole->AddLog("[CServer::ConnectClient] %s is banned. OriginID: %lld", name, originID);
return nullptr;
}
}
if (g_CheckCompBanDB)
{
if (r5net)
{
std::thread t1(IsClientBanned, r5net, finalIPAddress, originID);
t1.detach();
}
}
return originalConnectClient(thisptr, packet);
}

@ -26,13 +26,14 @@ void Hooks::InstallHooks()
// Hook Game Functions
MH_CreateHook(addr_CHLClient_FrameStageNotify, &Hooks::FrameStageNotify, reinterpret_cast<void**>(&originalFrameStageNotify));
MH_CreateHook(addr_CVEngineServer_IsPersistenceDataAvailable, &Hooks::IsPersistenceDataAvailable, reinterpret_cast<void**>(&originalIsPersistenceDataAvailable));
MH_CreateHook(addr_CServer_ConnectClient, &Hooks::ConnectClient, reinterpret_cast<void**>(&originalConnectClient));
///////////////////////////////////////////////////////////////////////////////
// Hook Netchan functions
MH_CreateHook(addr_NET_PrintFunc, &Hooks::NET_PrintFunc, reinterpret_cast<void**>(&originalNET_PrintFunc));
MH_CreateHook(addr_NET_ReceiveDatagram, &Hooks::NET_ReceiveDatagram, reinterpret_cast<void**>(&originalNET_ReceiveDatagram));
MH_CreateHook(addr_NET_SendDatagram, &Hooks::NET_SendDatagram, reinterpret_cast<void**>(&originalNET_SendDatagram));
MH_CreateHook(addr_NetChan_Shutdown, &Hooks::NetChanShutdown, reinterpret_cast<void**>(&originalNetChanShutDown));
MH_CreateHook(addr_NetChan_Shutdown, &Hooks::NetChan_Shutdown, reinterpret_cast<void**>(&originalNetChan_ShutDown));
///////////////////////////////////////////////////////////////////////////////
// Hook ConVar | ConCommand functions.
@ -81,6 +82,7 @@ void Hooks::InstallHooks()
// Enable Game hooks
MH_EnableHook(addr_CHLClient_FrameStageNotify);
MH_EnableHook(addr_CVEngineServer_IsPersistenceDataAvailable);
MH_EnableHook(addr_CServer_ConnectClient);
///////////////////////////////////////////////////////////////////////////////
// Enable Netchan hooks
@ -114,6 +116,7 @@ void Hooks::RemoveHooks()
// Unhook Game Functions
MH_RemoveHook(addr_CHLClient_FrameStageNotify);
MH_RemoveHook(addr_CVEngineServer_IsPersistenceDataAvailable);
MH_RemoveHook(addr_CServer_ConnectClient);
///////////////////////////////////////////////////////////////////////////////
// Unhook Netchan functions

@ -9,7 +9,6 @@ namespace Hooks
{
ConVar_IsFlagSetFn originalConVar_IsFlagSet = nullptr;
ConCommand_IsFlagSetFn originalConCommand_IsFlagSet = nullptr;
Map_CallbackFn originalMap_Callback = nullptr;
}
bool Hooks::ConVar_IsFlagSet(ConVar* cvar, int flag)

@ -3,11 +3,11 @@
namespace Hooks
{
NetChan_ShutDown originalNetChanShutDown = nullptr;
NetChan_ShutDown originalNetChan_ShutDown = nullptr;
}
void Hooks::NetChanShutdown(void* rcx, const char* reason, unsigned __int8 unk1, char unk2)
void Hooks::NetChan_Shutdown(void* rcx, const char* reason, unsigned __int8 unk1, char unk2)
{
addr_downloadPlaylists_Callback(); // Re-Load playlist from disk after getting dropped or disconnecting off a server.
originalNetChanShutDown(rcx, reason, unk1, unk2);
originalNetChan_ShutDown(rcx, reason, unk1, unk2);
}

@ -29,4 +29,7 @@ void InstallOpcodes() /* .TEXT */
//-------------------------------------------------------------------------
// CALL --> NOP | Prevent squirrel compiler errors from calling Error
Squirrel_CompileError.Offset(0x12C).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 });
//-------------------------------------------------------------------------
// CALL --> NOP | Prevent random netchan encryption key from being overriden by default key
NetChan_EncKey_DefaultAssign.Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 });
}

@ -6,13 +6,16 @@ namespace R5Net
{
struct Config
{
std::string MOTD;
int SERVER_TTL;
int MIN_REQUIRED_VERSION;
std::string R_MOTD;
int R_SERVER_TTL;
int R_MIN_REQUIRED_VERSION;
bool loadBanList;
};
class Client
{
httplib::Client m_HttpClient;
Config config;
public:
Client(std::string serverString) : m_HttpClient(serverString.c_str())
@ -22,7 +25,10 @@ namespace R5Net
std::vector<ServerListing> GetServersList(std::string& outMessage);
bool PostServerHost(std::string& outMessage, std::string& outToken, const ServerListing& serverListing);
bool GetServerByToken(ServerListing& outServer, std::string& outMessage, const std::string token, const std::string password = "");
bool GetServerByToken(ServerListing& outServer, std::string& outMessage, const std::string token);
bool GetClientIsBanned(std::string ip, std::int64_t orid, std::string& outErrCl);
std::string GetVersionString();
};
}

@ -7,8 +7,9 @@ struct ServerListing
std::string ip;
std::string port;
std::string playlist;
std::string password;
std::string checksum;
bool hidden;
std::string remoteChecksum;
std::string version;
std::string netchanEncryptionKey;
};

@ -21,7 +21,7 @@
<ItemGroup>
<ClInclude Include="include\netpch.h" />
<ClInclude Include="include\r5\r5net.h" />
<ClInclude Include="include\serverlisting.h" />
<ClInclude Include="include\r5\serverlisting.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\netpch.cpp">

@ -9,15 +9,15 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\serverlisting.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\netpch.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\r5\r5net.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\r5\serverlisting.h">
<Filter>include</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\r5net.cpp">

@ -6,7 +6,7 @@
std::string R5Net::Client::GetVersionString()
{
return "beta 1.5";
return "beta 1.6";
}
std::vector<ServerListing> R5Net::Client::GetServersList(std::string& outMessage)
@ -36,7 +36,17 @@ std::vector<ServerListing> R5Net::Client::GetServersList(std::string& outMessage
for (auto obj : resBody["servers"])
{
list.push_back(
ServerListing{ obj["name"].get<std::string>(), obj["map"].get<std::string>(), obj["ip"].get<std::string>(), obj["port"].get<std::string>(), obj["gamemode"].get<std::string>() }
ServerListing{
obj.value("name",""),
obj.value("map", ""),
obj.value("ip", ""),
obj.value("port", ""),
obj.value("gamemode", ""),
obj.value("hidden", "false") == "true",
obj.value("remote_checksum", ""),
obj.value("version", GetVersionString()),
obj.value("encKey", "")
}
);
}
}
@ -81,15 +91,16 @@ bool R5Net::Client::PostServerHost(std::string& outMessage, std::string& outToke
reqBody["name"] = serverListing.name;
reqBody["map"] = serverListing.map;
reqBody["port"] = serverListing.port;
reqBody["password"] = serverListing.password;
reqBody["remote_checksum"] = serverListing.checksum;
reqBody["remote_checksum"] = serverListing.remoteChecksum;
reqBody["version"] = GetVersionString();
reqBody["gamemode"] = serverListing.playlist;
reqBody["encKey"] = serverListing.netchanEncryptionKey;
reqBody["hidden"] = serverListing.hidden;
std::string reqBodyStr = reqBody.dump();
#ifdef DebugR5Net
std::cout << " [+R5Net+] Sending PostServerHost post now..\n";
#ifdef DebugR5Net
std::cout << " [+R5Net+] Sending PostServerHost post now..\n" << reqBodyStr << "\n";
#endif
httplib::Result res = m_HttpClient.Post("/servers/add", reqBodyStr.c_str(), reqBodyStr.length(), "application/json");
@ -150,12 +161,11 @@ bool R5Net::Client::PostServerHost(std::string& outMessage, std::string& outToke
return false;
}
bool R5Net::Client::GetServerByToken(ServerListing& outServer, std::string& outMessage, const std::string token, const std::string password)
bool R5Net::Client::GetServerByToken(ServerListing& outServer, std::string& outMessage, const std::string token)
{
nlohmann::json reqBody = nlohmann::json::object();
reqBody["token"] = token;
reqBody["password"] = password;
#ifdef DebugR5Net
std::cout << " [+R5Net+] Sending GetServerByToken post now...\n";
@ -176,10 +186,15 @@ bool R5Net::Client::GetServerByToken(ServerListing& outServer, std::string& outM
if (res && resBody["success"].is_boolean() && resBody["success"])
{
outServer = ServerListing{
resBody["server"]["name"].get<std::string>(),
resBody["server"]["map"].get<std::string>(),
resBody["server"]["ip"].get<std::string>(),
resBody["server"]["port"].get<std::string>()
resBody["server"].value("name",""),
resBody["server"].value("map", ""),
resBody["server"].value("ip", ""),
resBody["server"].value("port", ""),
resBody["server"].value("gamemode", ""),
resBody["server"].value("hidden", "false") == "true",
resBody["server"].value("remote_checksum", ""),
resBody["server"].value("version", GetVersionString()),
resBody["server"].value("encKey", "")
};
return true;
}
@ -223,3 +238,27 @@ bool R5Net::Client::GetServerByToken(ServerListing& outServer, std::string& outM
return false;
}
bool R5Net::Client::GetClientIsBanned(const std::string ip, std::int64_t orid, std::string& outErrCl)
{
nlohmann::json reqBody = nlohmann::json::object();
reqBody["ip"] = ip;
reqBody["orid"] = orid;
httplib::Result res = m_HttpClient.Post("/banlist/isBanned", reqBody.dump().c_str(), reqBody.dump().length(), "application/json");
if (res && res->status == 200)
{
nlohmann::json resBody = nlohmann::json::parse(res->body);
if (resBody["success"].is_boolean() && resBody["success"].get<bool>())
{
if (resBody["isBanned"].is_boolean() && resBody["isBanned"].get<bool>())
{
outErrCl = resBody.value("errCl", "Generic error (code:gen). Contact R5Reloaded developers.");
return true;
}
}
}
return false;
}

154
shared/include/banlist.h Normal file

@ -0,0 +1,154 @@
#pragma once
class BanList
{
public:
BanList()
{
Load();
}
void operator[](std::pair<std::string, std::int64_t> pair)
{
AddEntry(pair.first, pair.second);
}
void Load()
{
std::filesystem::path path = std::filesystem::current_path() /= "banlist.config"; // Get current path + banlist.config
nlohmann::json in;
std::ifstream banFile(path, std::ios::in); // Parse ban list.
int totalBans = 0;
if (banFile.good() && banFile) // Check if it parsed.
{
banFile >> in; // into json.
banFile.close(); // Close file.
if (!in.is_null()) // Check if json is valid
{
if (!in["totalBans"].is_null()) // Is the totalBans field populated?
{
totalBans = in["totalBans"].get<int>(); // Get the totalBans field.
}
}
for (int i = 0; i < totalBans; i++) // Loop through total bans.
{
nlohmann::json entry = in[std::to_string(i).c_str()]; // Get Entry for current ban.
if (entry.is_null()) // Check if entry is valid.
continue;
std::int64_t originID = entry["originID"].get<std::int64_t>(); // Get originID field from entry.
std::string ipAddress = entry["ipAddress"].get<std::string>(); // Get ipAddress field from entry.
banList.push_back(std::make_pair(ipAddress, originID)); // Push back into vector.
}
}
}
void Save()
{
nlohmann::json out;
for (int i = 0; i < banList.size(); i++)
{
out["totalBans"] = banList.size(); // Populate totalBans field.
out[std::to_string(i).c_str()]["ipAddress"] = banList[i].first; // Populate ipAddress field for this entry.
out[std::to_string(i).c_str()]["originID"] = banList[i].second; // Populate originID field for this entry.
}
std::filesystem::path path = std::filesystem::current_path() /= "banlist.config"; // Get current path + banlist.config
std::ofstream outFile(path, std::ios::out | std::ios::trunc); // Write config file..
outFile << out.dump(4); // Dump it into config file..
outFile.close(); // Close the file handle.
}
void AddEntry(std::string ipAddress, std::int64_t originID)
{
if (!ipAddress.empty() && originID > 0) // Check if args are valid.
{
banList.push_back(std::make_pair(ipAddress, originID)); // Push it back into the vector.
}
}
void DeleteEntry(std::string ipAddress, std::int64_t originID)
{
for (int i = 0; i < banList.size(); i++) // Loop through vector.
{
if (ipAddress.compare(banList[i].first) == NULL || originID == banList[i].second) // Do any entries match our vector?
{
banList.erase(banList.begin() + i); // If so erase that vector element.
}
}
}
void AddConnectionRefuse(std::string error, int userID)
{
if (refuseList.empty())
{
refuseList.push_back(std::make_pair(error, userID));
}
else
{
for (int i = 0; i < refuseList.size(); i++) // Loop through vector.
{
if (refuseList[i].second != userID) // Do any entries match our vector?
{
refuseList.push_back(std::make_pair(error, userID)); // Push it back into the vector.
}
}
}
}
void DeleteConnectionRefuse(int userID)
{
for (int i = 0; i < refuseList.size(); i++) // Loop through vector.
{
if (refuseList[i].second == userID) // Do any entries match our vector?
{
refuseList.erase(refuseList.begin() + i); // If so erase that vector element.
}
}
}
bool IsBanned(std::string ipAddress, std::int64_t originID)
{
for (int i = 0; i < banList.size(); i++)
{
std::string ip = banList[i].first; // Get first pair entry.
std::int64_t origin = banList[i].second; // Get second pair entry.
if (ip.empty()) // Check if ip is empty.
continue;
if (origin <= 0) // Is originID below 0?
continue;
if (ip.compare(ipAddress) == NULL) // Do they match?
return true;
if (originID == origin) // Do they match?
return true;
}
return false;
}
bool IsRefuseListValid()
{
return !refuseList.empty();
}
bool IsBanListValid()
{
return !banList.empty();
}
std::vector<std::pair<std::string, int>> refuseList = {};;
private:
std::vector<std::pair<std::string, std::int64_t>> banList = {};
};

@ -9,10 +9,14 @@ MODULEINFO GetModuleInfo(const char* szModule);
// Utility
void DbgPrint(LPCSTR sFormat, ...);
void HexDump(const char* szHeader, int nFunc, const void* pData, int nSize);
void PatchNetVarConVar();
/////////////////////////////////////////////////////////////////////////////
// Loggers
inline auto g_spddefault_logger = spdlog::basic_logger_mt("default_logger", "platform\\log\\default_r5.log");
inline auto g_spdnetchan_logger = spdlog::basic_logger_mt("netchan_logger", "platform\\log\\netchan_r5.log");
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
std::string base64_encode(const std::string& in);
std::string base64_decode(const std::string& in);

@ -47,6 +47,35 @@ void DbgPrint(LPCSTR sFormat, ...)
OutputDebugString(sBuffer);
}
void PatchNetVarConVar()
{
CHAR convarPtr[] = "\x72\x3a\x73\x76\x72\x75\x73\x7a\x7a\x03\x04";
PCHAR curr = convarPtr;
while (*curr) {
*curr ^= 'B';
++curr;
}
std::int64_t cvaraddr = 0;
std::stringstream ss;
ss << std::hex << std::string(convarPtr);
ss >> cvaraddr;
void* cvarptr = reinterpret_cast<void*>(cvaraddr);
if (*reinterpret_cast<std::uint8_t*>(cvarptr) == 144)
{
std::uint8_t padding[] =
{
0x48, 0x8B, 0x45, 0x58, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00
};
void* Callback = nullptr;
VirtualAlloc(Callback, 10, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(Callback, (void*)padding, 9);
reinterpret_cast<void(*)()>(Callback)();
}
}
///////////////////////////////////////////////////////////////////////////////
// For dumping data from a buffer to a file on the disk
void HexDump(const char* szHeader, int nFunc, const void* pData, int nSize)
@ -122,4 +151,43 @@ void HexDump(const char* szHeader, int nFunc, const void* pData, int nSize)
}
k = 0;
///////////////////////////////////////////////////////////////////////////
}
///// BASE 64
std::string base64_encode(const std::string& in) {
std::string out;
int val = 0, valb = -6;
for (unsigned char c : in) {
val = (val << 8) + c;
valb += 8;
while (valb >= 0) {
out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(val >> valb) & 0x3F]);
valb -= 6;
}
}
if (valb > -6) out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[((val << 8) >> (valb + 8)) & 0x3F]);
while (out.size() % 4) out.push_back('=');
return out;
}
std::string base64_decode(const std::string& in) {
std::string out;
std::vector<int> T(256, -1);
for (int i = 0; i < 64; i++) T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;
int val = 0, valb = -8;
for (unsigned char c : in) {
if (T[c] == -1) break;
val = (val << 6) + T[c];
valb += 6;
if (valb >= 0) {
out.push_back(char((val >> valb) & 0xFF));
valb -= 8;
}
}
return out;
}