Merge remote-tracking branch 'origin/develop' into 191-dedicated-server-optimization
This commit is contained in:
commit
3d2e8446f2
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -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
2
deps/libtomcrypt
vendored
@ -1 +1 @@
|
||||
Subproject commit ef023f3329c44ec8afca29b9a56bae684daa88d9
|
||||
Subproject commit c39390dba1dc519988fe8f6b1af6fdec24f86fc5
|
2
deps/protobuf
vendored
2
deps/protobuf
vendored
@ -1 +1 @@
|
||||
Subproject commit fa925b98937bafee6b5d7244cce5c1ad39bf9d36
|
||||
Subproject commit 17174b54ddd040a326dec6db75d1bfb5e5b3caa9
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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, ¶ms) && 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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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.
BIN
tools/protoc.exe
BIN
tools/protoc.exe
Binary file not shown.
Loading…
Reference in New Issue
Block a user