2017-05-30 07:45:19 -04:00
|
|
|
#include "STDInclude.hpp"
|
|
|
|
|
|
|
|
namespace Components
|
|
|
|
{
|
|
|
|
std::vector < std::string > CardTitles::CustomTitles;
|
|
|
|
Game::dvar_t* CardTitles::CustomTitleDvar;
|
|
|
|
|
|
|
|
CClient* CardTitles::GetClientByIndex(std::uint32_t index)
|
|
|
|
{
|
|
|
|
return reinterpret_cast<CClient*>(0x8E77B0 + (sizeof CClient * index));
|
|
|
|
}
|
|
|
|
|
|
|
|
std::int32_t CardTitles::GetPlayerCardClientInfo(std::int32_t lookupResult, playercarddata_s* data)
|
|
|
|
{
|
|
|
|
std::int32_t returnResult = lookupResult;
|
|
|
|
|
|
|
|
CClient* c;
|
|
|
|
std::string username = Dvar::Var("name").get<std::string>();
|
|
|
|
|
|
|
|
if (data->name == username)
|
|
|
|
{
|
|
|
|
returnResult += 0xFE000000;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (auto clientNum = 0; clientNum < 18; clientNum++)
|
|
|
|
{
|
|
|
|
c = GetClientByIndex(clientNum);
|
|
|
|
|
|
|
|
if (c != nullptr)
|
|
|
|
{
|
|
|
|
if (!strcmp(data->name, c->Name))
|
|
|
|
{
|
|
|
|
// Since a 4 byte integer is overkill for a row num: We can use it to store the customprefix + clientNum and use a 2 byte integer for the row number
|
|
|
|
returnResult += 0xFF000000;
|
|
|
|
returnResult += clientNum * 0x10000;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return returnResult;
|
|
|
|
}
|
|
|
|
void __declspec(naked) CardTitles::GetPlayerCardClientInfoStub()
|
|
|
|
{
|
|
|
|
__asm
|
|
|
|
{
|
|
|
|
push esi;
|
|
|
|
push eax;
|
|
|
|
call GetPlayerCardClientInfo;
|
2017-05-30 12:38:54 -04:00
|
|
|
add esp, 8;
|
2017-05-30 07:45:19 -04:00
|
|
|
|
|
|
|
pop esi;
|
|
|
|
pop ebp;
|
2017-05-30 12:38:54 -04:00
|
|
|
mov[ebx + 4], eax;
|
2017-05-30 07:45:19 -04:00
|
|
|
pop ebx;
|
|
|
|
|
2017-05-30 12:38:54 -04:00
|
|
|
push 62EB2Ch;
|
2017-05-30 07:45:19 -04:00
|
|
|
retn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-30 17:39:57 -04:00
|
|
|
const char* CardTitles::TableLookupByRowHook(Game::Operand* operand, tablelookuprequest_s* request)
|
2017-05-30 07:45:19 -04:00
|
|
|
{
|
2017-05-30 17:39:57 -04:00
|
|
|
std::uint8_t prefix = (request->tableRow >> (8 * 3)) & 0xFF;
|
|
|
|
std::uint8_t data = (request->tableRow >> (8 * 2)) & 0xFF;
|
|
|
|
const char* title = nullptr;
|
2017-05-30 07:45:19 -04:00
|
|
|
|
2017-05-30 17:39:57 -04:00
|
|
|
if (request->tablename == "mp/cardTitleTable.csv"s)
|
2017-05-30 07:45:19 -04:00
|
|
|
{
|
2017-05-30 17:39:57 -04:00
|
|
|
if (prefix != 0x00)
|
2017-05-30 07:45:19 -04:00
|
|
|
{
|
|
|
|
// Column 1 = CardTitle
|
2017-05-30 17:39:57 -04:00
|
|
|
if (request->tableColumn == 1)
|
|
|
|
{
|
|
|
|
if (prefix == 0xFE)
|
2017-05-30 07:45:19 -04:00
|
|
|
{
|
|
|
|
// 0xFF in front of the title to skip localization. Or else it will wait for a couple of seconds for the asset of type localize
|
2017-05-30 17:39:57 -04:00
|
|
|
if (*CustomTitleDvar->current.string)
|
2017-05-30 07:45:19 -04:00
|
|
|
{
|
2017-05-30 17:39:57 -04:00
|
|
|
title = Utils::String::VA("\x15%s", CustomTitleDvar->current.string);
|
|
|
|
|
|
|
|
// prepare return value
|
|
|
|
operand->internals.stringVal.string = title;
|
|
|
|
operand->dataType = Game::VAL_STRING;
|
|
|
|
|
|
|
|
return title;
|
2017-05-30 07:45:19 -04:00
|
|
|
}
|
|
|
|
}
|
2017-05-30 17:39:57 -04:00
|
|
|
else if (prefix == 0xFF)
|
2017-05-30 07:45:19 -04:00
|
|
|
{
|
2017-05-30 17:39:57 -04:00
|
|
|
if (!CustomTitles[data].empty())
|
2017-05-30 07:45:19 -04:00
|
|
|
{
|
2017-05-30 17:39:57 -04:00
|
|
|
title = Utils::String::VA("\x15%s", CustomTitles[data].data());
|
|
|
|
|
|
|
|
// prepare return value
|
|
|
|
operand->internals.stringVal.string = title;
|
|
|
|
operand->dataType = Game::VAL_STRING;
|
|
|
|
|
|
|
|
return title;
|
2017-05-30 07:45:19 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-05-30 17:39:57 -04:00
|
|
|
|
2017-05-30 07:45:19 -04:00
|
|
|
// If the title was changed it already returned at this point so...
|
|
|
|
// Remove prefix and data to make being readable to the normal lookuprequest
|
2017-05-30 17:39:57 -04:00
|
|
|
request->tableRow = static_cast<std::int32_t>(*(reinterpret_cast<WORD*>(&request->tableRow)));
|
2017-05-30 07:45:19 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-30 17:39:57 -04:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void __declspec(naked) CardTitles::TableLookupByRowHookStub()
|
|
|
|
{
|
2017-05-30 07:45:19 -04:00
|
|
|
__asm
|
|
|
|
{
|
2017-05-30 17:39:57 -04:00
|
|
|
push esi;
|
|
|
|
push ebx;
|
|
|
|
|
|
|
|
pushad;
|
|
|
|
call TableLookupByRowHook;
|
|
|
|
cmp eax, 0;
|
|
|
|
popad;
|
|
|
|
|
|
|
|
jz OriginalTitle;
|
|
|
|
|
|
|
|
add esp, 8;
|
|
|
|
|
|
|
|
pop ecx;
|
|
|
|
mov ecx, DWORD ptr[esi + 4];
|
|
|
|
retn;
|
|
|
|
|
|
|
|
OriginalTitle:
|
|
|
|
|
|
|
|
add esp, 8;
|
|
|
|
|
2017-05-30 12:38:54 -04:00
|
|
|
mov eax, [esi + 50h];
|
|
|
|
cmp eax, 3;
|
2017-05-30 07:45:19 -04:00
|
|
|
|
2017-05-30 12:38:54 -04:00
|
|
|
push 62DCC7h;
|
2017-05-30 07:45:19 -04:00
|
|
|
retn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-30 18:27:08 -04:00
|
|
|
void CardTitles::SendCustomTitlesToClients()
|
|
|
|
{
|
|
|
|
const char* list = "";
|
|
|
|
|
|
|
|
for (int i = 0; i < 18; i++)
|
|
|
|
{
|
2017-05-30 19:42:25 -04:00
|
|
|
char playerTitle[18];
|
2017-05-30 18:27:08 -04:00
|
|
|
|
|
|
|
if (Game::svs_clients[i].state >= 3)
|
|
|
|
{
|
2017-05-30 19:42:25 -04:00
|
|
|
strncpy_s(playerTitle, Game::Info_ValueForKey(Game::svs_clients[i].connectInfoString, "customTitle"), 17);
|
2017-05-30 18:27:08 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
memset(playerTitle, 0, 18);
|
|
|
|
}
|
|
|
|
|
|
|
|
list = Utils::String::VA("%s\\%s\\%s", list, std::to_string(i).c_str(), playerTitle);
|
|
|
|
}
|
|
|
|
|
2017-05-30 19:42:25 -04:00
|
|
|
list = Utils::String::VA("%c customTitles \"%s\"", 21, list);
|
2017-05-30 18:27:08 -04:00
|
|
|
Game::SV_GameSendServerCommand(-1, 0, list);
|
|
|
|
}
|
|
|
|
|
2017-05-30 09:37:50 -04:00
|
|
|
void CardTitles::ParseCustomTitles(const char* msg)
|
2017-05-30 08:29:09 -04:00
|
|
|
{
|
2017-05-30 09:37:50 -04:00
|
|
|
const char* playerTitle;
|
2017-05-30 08:29:09 -04:00
|
|
|
for (int i = 0; i < 18; i++)
|
|
|
|
{
|
2017-05-30 18:27:08 -04:00
|
|
|
playerTitle = Game::Info_ValueForKey(msg, std::to_string(i).c_str());
|
2017-05-30 08:29:09 -04:00
|
|
|
|
|
|
|
if (playerTitle != nullptr)
|
|
|
|
{
|
|
|
|
CustomTitles[i] = playerTitle;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CustomTitles[i] = "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-30 07:45:19 -04:00
|
|
|
CardTitles::CardTitles()
|
|
|
|
{
|
|
|
|
Dvar::OnInit([]() {
|
2017-05-30 19:42:25 -04:00
|
|
|
CardTitles::CustomTitleDvar = Game::Dvar_RegisterString("customtitle", "", Game::dvar_flag::DVAR_FLAG_USERINFO | Game::dvar_flag::DVAR_FLAG_SAVED, "Custom card title");
|
2017-05-30 07:45:19 -04:00
|
|
|
});
|
|
|
|
|
2017-05-30 19:42:25 -04:00
|
|
|
ServerCommands::OnCommand(21, [](Command::Params* params) {
|
2017-05-30 08:29:09 -04:00
|
|
|
|
|
|
|
if (params->get(1) == "customTitles"s && !Flags::HasFlag("dedicated"))
|
|
|
|
{
|
|
|
|
if (params->length() == 3)
|
|
|
|
{
|
|
|
|
CardTitles::ParseCustomTitles(params->get(2));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
2017-05-30 07:45:19 -04:00
|
|
|
CardTitles::CustomTitles.resize(18);
|
|
|
|
|
|
|
|
Utils::Hook(0x62EB26, GetPlayerCardClientInfoStub).install()->quick();
|
|
|
|
|
2017-05-30 17:39:57 -04:00
|
|
|
// Table lookup stuff
|
|
|
|
Utils::Hook(0x62DCC1, TableLookupByRowHookStub).install()->quick();
|
2017-05-30 07:45:19 -04:00
|
|
|
Utils::Hook::Nop(0x62DCC6, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
CardTitles::~CardTitles()
|
|
|
|
{
|
2017-05-30 12:38:54 -04:00
|
|
|
CustomTitles.clear();
|
2017-05-30 07:45:19 -04:00
|
|
|
}
|
|
|
|
}
|