iw4x-client/src/Components/Modules/CardTitles.cpp

219 lines
4.9 KiB
C++
Raw Normal View History

2022-02-27 07:53:44 -05:00
#include <STDInclude.hpp>
namespace Components
{
2017-05-31 03:59:03 -04:00
std::string CardTitles::CustomTitles[18];
Dvar::Var CardTitles::CustomTitle;
CClient* CardTitles::GetClientByIndex(std::uint32_t index)
{
2017-05-31 04:33:18 -04:00
return &reinterpret_cast<CClient*>(0x8E77B0)[index];
}
std::int32_t CardTitles::GetPlayerCardClientInfo(std::int32_t lookupResult, Game::PlayerCardData* data)
{
std::int32_t returnResult = lookupResult;
std::string username = Dvar::Var("name").get<std::string>();
if (data->name == username)
{
returnResult += 0xFE000000;
}
else
{
for (std::size_t clientNum = 0; clientNum < Game::MAX_CLIENTS; ++clientNum)
{
2017-05-31 03:59:03 -04:00
CClient* c = GetClientByIndex(clientNum);
if (c != nullptr)
{
if (!std::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;
}
2017-05-31 03:59:03 -04:00
void __declspec(naked) CardTitles::GetPlayerCardClientInfoStub()
{
__asm
{
2017-05-31 03:59:03 -04:00
push eax
pushad
2017-05-31 03:59:03 -04:00
push esi
push eax
call GetPlayerCardClientInfo
add esp, 8
2017-05-31 03:59:03 -04:00
mov [esp + 20h], eax
popad
pop eax
pop esi
pop ebp
mov [ebx + 4], eax
2017-05-31 03:59:03 -04:00
pop ebx
retn
}
}
const char* CardTitles::TableLookupByRowHook(Game::Operand* operand, tablelookuprequest_s* request)
{
std::uint8_t prefix = (request->tableRow >> (8 * 3)) & 0xFF;
std::uint8_t data = (request->tableRow >> (8 * 2)) & 0xFF;
2017-06-02 09:36:20 -04:00
if (data >= ARRAYSIZE(CardTitles::CustomTitles)) return nullptr;
if (request->tablename == "mp/cardTitleTable.csv"s)
{
if (prefix != 0x00)
{
// Column 1 = CardTitle
if (request->tableColumn == 1)
{
if (prefix == 0xFE)
{
if (!CardTitles::CustomTitle.get<std::string>().empty())
2017-05-31 04:33:18 -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
const char* title = Utils::String::VA("\x15%s", CardTitles::CustomTitle.get<const char*>());
2017-05-31 04:33:18 -04:00
// prepare return value
operand->internals.stringVal.string = title;
operand->dataType = Game::VAL_STRING;
2017-05-31 04:33:18 -04:00
return title;
}
}
else if (prefix == 0xFF)
{
2017-06-02 09:36:20 -04:00
if (!CardTitles::CustomTitles[data].empty())
{
2017-06-02 09:36:20 -04:00
const char* title = Utils::String::VA("\x15%s", CardTitles::CustomTitles[data].data());
// prepare return value
operand->internals.stringVal.string = title;
operand->dataType = Game::VAL_STRING;
return title;
}
}
}
// If the title was changed it already returned at this point so...
// Remove prefix and data to make being readable to the normal lookuprequest
request->tableRow = static_cast<std::int32_t>(*(reinterpret_cast<WORD*>(&request->tableRow)));
}
}
return nullptr;
}
2017-05-31 04:33:18 -04:00
__declspec(naked) void CardTitles::TableLookupByRowHookStub()
{
__asm
{
2017-05-31 04:33:18 -04:00
push eax
pushad
2017-05-31 04:33:18 -04:00
push esi
push ebx
2017-05-31 04:33:18 -04:00
call TableLookupByRowHook
add esp, 8
2017-05-31 04:33:18 -04:00
mov [esp + 20h], eax
popad
pop eax
2017-05-31 04:33:18 -04:00
cmp eax, 0
jz OriginalTitle
2017-05-31 04:33:18 -04:00
pop ecx
mov ecx, DWORD ptr[esi + 4]
retn
OriginalTitle:
2017-05-31 04:33:18 -04:00
mov eax, [esi + 50h]
cmp eax, 3
2017-05-31 04:33:18 -04:00
push 62DCC7h
retn
}
}
void CardTitles::SendCustomTitlesToClients()
{
2017-05-31 04:33:18 -04:00
std::string list;
for (std::size_t i = 0; i < Game::MAX_CLIENTS; i++)
{
2017-05-30 19:42:25 -04:00
char playerTitle[18];
2022-08-20 06:09:41 -04:00
if (Game::svs_clients[i].header.state >= Game::CS_CONNECTED)
{
strncpy_s(playerTitle, Game::Info_ValueForKey(Game::svs_clients[i].userinfo, "customTitle"), _TRUNCATE);
}
else
{
playerTitle[0] = '\0';
}
list.append(std::format("\\{}\\{}", std::to_string(i), playerTitle));
}
const auto* command = Utils::String::Format("{:c} customTitles \"{}\"", 21, list);
2022-05-31 11:57:44 -04:00
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, command);
}
void CardTitles::ParseCustomTitles(const char* msg)
{
for (std::size_t i = 0; i < Game::MAX_CLIENTS; ++i)
{
2017-05-31 03:59:03 -04:00
const char* playerTitle = Game::Info_ValueForKey(msg, std::to_string(i).c_str());
2017-05-31 04:33:18 -04:00
if (playerTitle) CardTitles::CustomTitles[i] = playerTitle;
else CardTitles::CustomTitles[i].clear();
}
}
CardTitles::CardTitles()
{
Scheduler::Once([]
2017-05-31 03:59:03 -04:00
{
CardTitles::CustomTitle = Dvar::Register<const char*>("customTitle", "", Game::DVAR_USERINFO | Game::DVAR_ARCHIVE, "Custom card title");
}, Scheduler::Pipeline::MAIN);
2017-05-31 03:59:03 -04:00
ServerCommands::OnCommand(21, [](Command::Params* params)
{
if (params->get(1) == "customTitles"s && !Dedicated::IsEnabled())
{
2022-03-17 14:50:20 -04:00
if (params->size() == 3)
{
CardTitles::ParseCustomTitles(params->get(2));
return true;
}
}
return false;
});
2017-05-31 04:33:18 -04:00
Utils::Hook(0x62EB26, CardTitles::GetPlayerCardClientInfoStub).install()->quick();
// Table lookup stuff
2017-05-31 04:33:18 -04:00
Utils::Hook(0x62DCC1, CardTitles::TableLookupByRowHookStub).install()->quick();
Utils::Hook::Nop(0x62DCC6, 1);
}
2017-05-31 04:33:18 -04:00
}