Merge remote-tracking branch 'origin/develop' into 191-dedicated-server-optimization

This commit is contained in:
sr0 2017-02-23 21:37:22 +01:00
commit 3d2e8446f2
20 changed files with 229 additions and 109 deletions

2
.gitmodules vendored
View File

@ -34,7 +34,7 @@
[submodule "deps/protobuf"]
path = deps/protobuf
url = https://github.com/google/protobuf.git
branch = 3.2.x
branch = master
[submodule "deps/WinToast"]
path = deps/WinToast
url = https://github.com/mohabouje/WinToast.git

2
deps/libtomcrypt vendored

@ -1 +1 @@
Subproject commit ef023f3329c44ec8afca29b9a56bae684daa88d9
Subproject commit c39390dba1dc519988fe8f6b1af6fdec24f86fc5

2
deps/protobuf vendored

@ -1 +1 @@
Subproject commit fa925b98937bafee6b5d7244cce5c1ad39bf9d36
Subproject commit 17174b54ddd040a326dec6db75d1bfb5e5b3caa9

View File

@ -26,7 +26,7 @@ namespace Components
char** Console::GetAutoCompleteFileList(const char *path, const char *extension, Game::FsListBehavior_e behavior, int *numfiles, int allocTrackType)
{
if (path == reinterpret_cast<char*>(0xBAADF00D) || path == reinterpret_cast<char*>(0xCDCDCDCD) || IsBadReadPtr(path, 1)) return nullptr;
if (path == reinterpret_cast<char*>(0xBAADF00D) || path == reinterpret_cast<char*>(0xCDCDCDCD) || ::Utils::Memory::IsBadReadPtr(path)) return nullptr;
return Game::FS_GetFileList(path, extension, behavior, numfiles, allocTrackType);
}

View File

@ -569,6 +569,8 @@ namespace Components
HRESULT D3D9Ex::D3D9Device::SetPixelShaderConstantF(UINT StartRegister, CONST float* pConstantData, UINT Vector4fCount)
{
// Use real bad readptr check here, cause the query takes too long
// TODO: Fix the actual error!
if (IsBadReadPtr(pConstantData, Vector4fCount * 16))
{
//Logger::Print("Invalid shader constant array!\n");

View File

@ -251,7 +251,14 @@ namespace Components
int FileSystem::ExecIsFSStub(const char* execFilename)
{
return !File(execFilename).exists();
bool result = !File(execFilename).exists();
if(execFilename =="mp/stats_init.cfg"s)
{
OutputDebugStringA("");
}
return result;
}
void FileSystem::FsStartupSync(const char* a1)
@ -290,6 +297,9 @@ namespace Components
// Filesystem config checks
Utils::Hook(0x6098FD, FileSystem::ExecIsFSStub, HOOK_CALL).install()->quick();
// Don't strip the folders from the config name (otherwise our ExecIsFSStub fails)
Utils::Hook::Nop(0x6098F2, 5);
// Register additional folders
Utils::Hook(0x482647, FileSystem::StartupStub, HOOK_JUMP).install()->quick();

View File

@ -260,6 +260,25 @@ namespace Components
Utils::Hook(0x4CE5EE, Localization::SetStringStub, HOOK_CALL).install()->quick();
Localization::UseLocalization = Dvar::Register<bool>("ui_localize", true, Game::dvar_flag::DVAR_FLAG_NONE, "Use localization strings");
// Generate localized entries for custom classes above 10
AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader asset, std::string name, bool* /*restrict*/)
{
if (type != Game::XAssetType::ASSET_TYPE_LOCALIZE_ENTRY) return;
if(name == "CLASS_SLOT1"s)
{
for(int i = 11; i <= NUM_CUSTOM_CLASSES; ++i)
{
std::string key = Utils::String::VA("CLASS_SLOT%i", i);
std::string value = asset.localize->value;
Utils::String::Replace(value, "1", Utils::String::VA("%i", i)); // Pretty ugly, but it should work
Localization::Set(key, value);
}
}
});
}
Localization::~Localization()

View File

@ -276,7 +276,17 @@ namespace Components
{
for (auto menu : Menus::CustomMenus)
{
Utils::Merge(&menus, Menus::LoadMenu(menu));
bool hasMenu = false;
for(auto &loadedMenu : menus)
{
if(loadedMenu->window.name == menu)
{
hasMenu = true;
break;
}
}
if(!hasMenu) Utils::Merge(&menus, Menus::LoadMenu(menu));
}
}
@ -710,6 +720,7 @@ namespace Components
Menus::Add("ui_mp/startup_messages.menu");
Menus::Add("ui_mp/pc_store.menu");
Menus::Add("ui_mp/iw4x_credits.menu");
Menus::Add("ui_mp/resetclass.menu");
}
Menus::~Menus()

View File

@ -17,6 +17,7 @@ namespace Components
Utils::Hook(0x59A6F8, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick();
Utils::Hook(0x57F1E6, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick();
Utils::Hook(0x57ED36, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick();
//Utils::Hook(0x609832, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick();
// remove fs_game check for moddable rawfiles - allows non-fs_game to modify rawfiles
Utils::Hook::Nop(0x61AB76, 2);

View File

@ -116,9 +116,41 @@ namespace Components
}
}
bool StructuredData::UpdateVersionOffsets(Game::StructuredDataDefSet *set, Game::StructuredDataBuffer *buffer, Game::StructuredDataDef *whatever)
{
Game::StructuredDataDef* newDef = &set->defs[0];
Game::StructuredDataDef* oldDef = &set->defs[0];
for(unsigned int i = 0; i < set->defCount; ++i)
{
if(newDef->version < set->defs[i].version)
{
newDef = &set->defs[i];
}
if(set->defs[i].version == *reinterpret_cast<int*>(buffer->data))
{
oldDef = &set->defs[i];
}
}
if (newDef->version >= 159 && oldDef->version <= 158)
{
// this should move the data 320 bytes infront
std::memmove(&buffer->data[3963], &buffer->data[3643], oldDef->size - 3643);
}
// StructuredData_UpdateVersion
return Utils::Hook::Call<bool(void*, void*, void*)>(0x456830)(set, buffer, whatever);
}
StructuredData::StructuredData()
{
Utils::Hook::Set<BYTE>(0x60A2FE, 15); // 15 custom classes
// Correctly upgrade stats
Utils::Hook(0x42F088, StructuredData::UpdateVersionOffsets, HOOK_CALL).install()->quick();
// 15 or more custom classes
Utils::Hook::Set<BYTE>(0x60A2FE, NUM_CUSTOM_CLASSES);
// Only execute this when building zones
if (!ZoneBuilder::IsEnabled()) return;
@ -126,7 +158,7 @@ namespace Components
AssetHandler::OnLoad([] (Game::XAssetType type, Game::XAssetHeader asset, std::string filename, bool* /*restrict*/)
{
// Only intercept playerdatadef loading
if (filename != "mp/playerdata.def" || type != Game::XAssetType::ASSET_TYPE_STRUCTUREDDATADEF) return;
if (type != Game::XAssetType::ASSET_TYPE_STRUCTUREDDATADEF || filename != "mp/playerdata.def") return;
// Store asset
Game::StructuredDataDefSet* data = asset.structuredData;

View File

@ -31,6 +31,8 @@ namespace Components
#endif
private:
static bool UpdateVersionOffsets(Game::StructuredDataDefSet *set, Game::StructuredDataBuffer *buffer, Game::StructuredDataDef *oldDef);
static void PatchPlayerDataEnum(Game::StructuredDataDef* data, PlayerDataType type, std::vector<std::string>& entries);
static void PatchAdditionalData(Game::StructuredDataDef* data, std::unordered_map<std::string, std::string>& patches);

View File

@ -1,6 +1,7 @@
#pragma once
#define PROTOCOL 0x93
#define NUM_CUSTOM_CLASSES 15
// This allows us to compile our structures in IDA, for easier reversing :3
#ifdef __cplusplus
@ -1553,6 +1554,12 @@ namespace Game
StructuredDataDef *defs;
};
struct StructuredDataBuffer
{
char *data;
size_t size; // 8188
};
typedef struct
{
StructuredDataDef* data;

View File

@ -67,6 +67,10 @@ AssertSize(std::uint8_t, 1);
// Ensure pointers are 4 bytes in size (32-bit)
static_assert(sizeof(intptr_t) == 4 && sizeof(void*) == 4 && sizeof(size_t) == 4, "This doesn't seem to be a 32-bit environment!");
#if !defined(_M_IX86)
#error "Invalid processor achritecture!"
#endif
extern "C"
{
// Disable telemetry data logging

View File

@ -49,6 +49,7 @@ template <size_t S> class Sizer { };
#pragma warning(disable: 4005)
#pragma warning(disable: 4091)
#pragma warning(disable: 4100)
#pragma warning(disable: 4244)
#pragma warning(disable: 4389)
#pragma warning(disable: 4702)
#pragma warning(disable: 4996) // _CRT_SECURE_NO_WARNINGS

View File

@ -28,63 +28,87 @@ namespace Steam
std::function<Proxy::SteamFreeLastCallbackFn> Proxy::SteamFreeLastCallback;
std::function<Proxy::SteamGetAPICallResultFn> Proxy::SteamGetAPICallResult;
void* Interface::getMethod(std::string method)
std::pair<void*, uint16_t> Interface::getMethod(std::string method)
{
if(this->methodCache.find(method) != this->methodCache.end())
{
return this->methodCache[method];
}
void* methodPtr = Interface::lookupMethod(method);
this->methodCache[method] = methodPtr;
return methodPtr;
auto methodData = Interface::lookupMethod(method);
this->methodCache[method] = methodData;
return methodData;
}
void* Interface::lookupMethod(std::string method)
std::pair<void*, uint16_t> Interface::lookupMethod(std::string method)
{
if (!::Utils::Memory::IsBadReadPtr(this->interfacePtr))
{
if (IsBadReadPtr(this->interfacePtr, 4)) return nullptr;
unsigned char** vftbl = *static_cast<unsigned char***>(this->interfacePtr);
while (!IsBadReadPtr(vftbl, 4) && !IsBadCodePtr((FARPROC(*vftbl))))
while (!::Utils::Memory::IsBadReadPtr(vftbl) && !::Utils::Memory::IsBadCodePtr((FARPROC(*vftbl))))
{
if(this->getMethodName(*vftbl) == method) return *vftbl;
std::string name;
uint16_t params;
if (this->getMethodData(*vftbl, &name, &params) && name == method)
{
return{ *vftbl, params };
}
++vftbl;
}
return nullptr;
}
size_t Interface::getMethodParamSize(void* method)
{
unsigned char* methodPtr = static_cast<unsigned char*>(method);
for (; !IsBadReadPtr(methodPtr, 3); ++methodPtr)
{
if (methodPtr[0] == 0xC2 && methodPtr[2] == 0) // __stdcall return
{
return (static_cast<size_t>(methodPtr[1])/* / sizeof(void*)*/);
}
return { nullptr, 0 };
}
return 0;
bool Interface::getMethodData(unsigned char* methodPtr, std::string* name, uint16_t* params)
{
name->clear();
*params = 0;
if (::Utils::Memory::IsBadCodePtr(methodPtr)) return false;
ud_t ud;
ud_init(&ud);
ud_set_mode(&ud, 32);
ud_set_pc(&ud, reinterpret_cast<uint64_t>(methodPtr));
ud_set_input_buffer(&ud, reinterpret_cast<uint8_t*>(methodPtr), INT32_MAX);
while (true)
{
ud_disassemble(&ud);
if (ud_insn_mnemonic(&ud) == UD_Iret)
{
const ud_operand* operand = ud_insn_opr(&ud, 0);
if (!operand) break;
if (operand->type == UD_OP_IMM && operand->size == 16)
{
*params = operand->lval.uword;
return true;
}
std::string Interface::getMethodName(unsigned char* methodPtr)
{
for(;!IsBadReadPtr(methodPtr, 3); ++methodPtr)
{
if(methodPtr[0] == 0x68) // Push
{
char* name = *reinterpret_cast<char**>(&methodPtr[1]);
if(!IsBadReadPtr(name, 1)) return name;
}
else if(methodPtr[0] == 0xC2 && methodPtr[2] == 0) // __stdcall return
{
break;
}
if (ud_insn_mnemonic(&ud) == UD_Ipush && name->empty())
{
auto operand = ud_insn_opr(&ud, 0);
if (operand->type == UD_OP_IMM && operand->size == 32)
{
char* operandPtr = reinterpret_cast<char*>(operand->lval.udword);
if (!::Utils::Memory::IsBadReadPtr(operandPtr))
{
name->clear();
name->append(operandPtr);
}
}
}
}
return "";
return false;
}
void Proxy::SetGame(uint32_t appId)
@ -123,23 +147,6 @@ namespace Steam
char* modId = "IW4x";
gameID.modID = *reinterpret_cast<unsigned int*>(modId) | 0x80000000;
// Interface clientApps(Proxy::ClientEngine->GetIClientApps(Proxy::SteamUser, Proxy::SteamPipe, "CLIENTAPPS_INTERFACE_VERSION001"));
// Interface clientShortcuts(Proxy::ClientEngine->GetIClientShortcuts(Proxy::SteamUser, Proxy::SteamPipe, "CLIENTSHORTCUTS_INTERFACE_VERSION001"));
// if (!clientApps || !clientShortcuts) return;
//
// KeyValuesBuilder builder;
// builder.packString("name", mod.data());
// builder.packUint64("gameid", gameID.bits);
// builder.packString("installed", "1");
// builder.packString("gamedir", "IW4x");
// builder.packString("serverbrowsername", "IW4x");
// builder.packEnd();
//
// std::string str = builder.getString();
// uint32_t uniqueId = clientShortcuts.invoke<uint32_t>("GetUniqueLocalAppId");
// if (clientApps.invoke<bool>("SetLocalAppConfig", uniqueId, str.data(), static_cast<uint32_t>(str.size())))
{
Interface clientUtils(Proxy::ClientEngine->GetIClientUtils(Proxy::SteamPipe, "CLIENTUTILS_INTERFACE_VERSION001"));
clientUtils.invoke<void>("SetAppIDForCurrentPipe", Proxy::AppId, false);
@ -167,7 +174,6 @@ namespace Steam
else if (expectedParams == 48) // Legacy, expects VAC blob
{
char blob[8] = { 0 };
Proxy::ClientUser.invoke<bool>("SpawnProcess", blob, 0, ourPath, cmdline.data(), 0, ourDirectory, gameID.bits, Proxy::AppId, mod.data(), 0, 0);
}
else
@ -175,7 +181,6 @@ namespace Steam
OutputDebugStringA("Steam proxy was unable to match the arguments for SpawnProcess!\n");
}
}
}
void Proxy::RunMod()
{
@ -322,11 +327,7 @@ namespace Steam
void Proxy::StartSteamIfNecessary()
{
if (Proxy::GetSteamDirectory().empty()
#ifndef DEBUG
|| !Steam::Enabled()
#endif
) return;
if (Proxy::GetSteamDirectory().empty() || !Steam::Enabled()) return;
HKEY hRegKey;
DWORD pid = 0;

View File

@ -146,14 +146,14 @@ namespace Steam
return T();
}
void* method = this->getMethod(methodName);
if (!method)
auto method = this->getMethod(methodName);
if (!method.first)
{
OutputDebugStringA(::Utils::String::VA("Steam interface method %s not found!\n", methodName.data()));
return T();
}
size_t argc = this->getMethodParamSize(method);
size_t argc = method.second;
constexpr size_t passedArgc = Interface::AddSizes<sizeof(Args)...>::value;
if(passedArgc != argc)
{
@ -161,7 +161,7 @@ namespace Steam
return T();
}
return reinterpret_cast<T(__thiscall*)(void*, Args ...)>(method)(this->interfacePtr, args...);
return reinterpret_cast<T(__thiscall*)(void*, Args ...)>(method.first)(this->interfacePtr, args...);
}
explicit operator bool() const
@ -171,14 +171,12 @@ namespace Steam
size_t paramSize(std::string methodName)
{
void* method = this->getMethod(methodName);
if (method) return this->getMethodParamSize(method);
return 0;
auto method = this->getMethod(methodName);
return method.second;
}
private:
// TODO: Use fold expressions in C++17 once available
// TODO: Use fold expressions once available (C++17)
template<std::size_t ...>
struct AddSizes : std::integral_constant<std::size_t, 0> {};
@ -187,11 +185,10 @@ namespace Steam
struct AddSizes<X, Xs...> : std::integral_constant<std::size_t, X + ((AddSizes<Xs...>::value + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1))> {};
void* interfacePtr;
std::unordered_map<std::string, void*> methodCache;
void* getMethod(std::string method);
void* lookupMethod(std::string method);
size_t getMethodParamSize(void* method);
std::string getMethodName(unsigned char* methodPtr);
std::unordered_map<std::string, std::pair<void*, uint16_t>> methodCache;
std::pair<void*, uint16_t> getMethod(std::string method);
std::pair<void*, uint16_t> lookupMethod(std::string method);
bool getMethodData(unsigned char* methodPtr, std::string* name, uint16_t* params);
};
class KeyValuesBuilder

View File

@ -64,4 +64,34 @@ namespace Utils
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;
}
}

View File

@ -130,5 +130,8 @@ namespace Utils
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);
};
}

Binary file not shown.

Binary file not shown.