[Threading]: Attempt to resolve a crash... (#561)
* [Threading]: Attempt to resolve a crash... * [Main]: Add srand * [General]: Use std:: * [Party]: Fix legacy q3 protocol
This commit is contained in:
parent
75d53bea64
commit
2acd584723
@ -71,11 +71,9 @@ namespace Components
|
|||||||
Loader::Register(new Dedicated());
|
Loader::Register(new Dedicated());
|
||||||
Loader::Register(new Discovery());
|
Loader::Register(new Discovery());
|
||||||
Loader::Register(new FastFiles());
|
Loader::Register(new FastFiles());
|
||||||
Loader::Register(new FrameTime());
|
|
||||||
Loader::Register(new Gametypes());
|
Loader::Register(new Gametypes());
|
||||||
Loader::Register(new Materials());
|
Loader::Register(new Materials());
|
||||||
Loader::Register(new Scheduler());
|
Loader::Register(new Scheduler());
|
||||||
Loader::Register(new Threading());
|
|
||||||
Loader::Register(new CardTitles());
|
Loader::Register(new CardTitles());
|
||||||
Loader::Register(new FileSystem());
|
Loader::Register(new FileSystem());
|
||||||
Loader::Register(new ModelSurfs());
|
Loader::Register(new ModelSurfs());
|
||||||
@ -90,7 +88,6 @@ namespace Components
|
|||||||
Loader::Register(new ZoneBuilder());
|
Loader::Register(new ZoneBuilder());
|
||||||
Loader::Register(new AssetHandler());
|
Loader::Register(new AssetHandler());
|
||||||
Loader::Register(new Localization());
|
Loader::Register(new Localization());
|
||||||
//Loader::Register(new MusicalTalent());
|
|
||||||
Loader::Register(new ServerCommands());
|
Loader::Register(new ServerCommands());
|
||||||
Loader::Register(new StructuredData());
|
Loader::Register(new StructuredData());
|
||||||
Loader::Register(new ConnectProtocol());
|
Loader::Register(new ConnectProtocol());
|
||||||
|
@ -103,11 +103,9 @@ namespace Components
|
|||||||
#include "Modules/Discovery.hpp"
|
#include "Modules/Discovery.hpp"
|
||||||
#include "Modules/Exception.hpp"
|
#include "Modules/Exception.hpp"
|
||||||
#include "Modules/FastFiles.hpp"
|
#include "Modules/FastFiles.hpp"
|
||||||
#include "Modules/FrameTime.hpp"
|
|
||||||
#include "Modules/Gametypes.hpp"
|
#include "Modules/Gametypes.hpp"
|
||||||
#include "Modules/Materials.hpp"
|
#include "Modules/Materials.hpp"
|
||||||
#include "Modules/Singleton.hpp"
|
#include "Modules/Singleton.hpp"
|
||||||
#include "Modules/Threading.hpp"
|
|
||||||
#include "Modules/CardTitles.hpp"
|
#include "Modules/CardTitles.hpp"
|
||||||
#include "Modules/FileSystem.hpp"
|
#include "Modules/FileSystem.hpp"
|
||||||
#include "Modules/ModelSurfs.hpp"
|
#include "Modules/ModelSurfs.hpp"
|
||||||
@ -120,7 +118,6 @@ namespace Components
|
|||||||
#include "Modules/ZoneBuilder.hpp"
|
#include "Modules/ZoneBuilder.hpp"
|
||||||
#include "Modules/AssetHandler.hpp"
|
#include "Modules/AssetHandler.hpp"
|
||||||
#include "Modules/Localization.hpp"
|
#include "Modules/Localization.hpp"
|
||||||
#include "Modules/MusicalTalent.hpp"
|
|
||||||
#include "Modules/ServerCommands.hpp"
|
#include "Modules/ServerCommands.hpp"
|
||||||
#include "Modules/StructuredData.hpp"
|
#include "Modules/StructuredData.hpp"
|
||||||
#include "Modules/ConnectProtocol.hpp"
|
#include "Modules/ConnectProtocol.hpp"
|
||||||
|
@ -42,16 +42,21 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Bullet::BG_srand_Hk(unsigned int* pHoldrand)
|
||||||
|
{
|
||||||
|
*pHoldrand = static_cast<unsigned int>(std::rand());
|
||||||
|
}
|
||||||
|
|
||||||
Bullet::Bullet()
|
Bullet::Bullet()
|
||||||
{
|
{
|
||||||
BGSurfacePenetration = Dvar::Register<float>("bg_surfacePenetration", 0.0f,
|
BGSurfacePenetration = Dvar::Register<float>("bg_surfacePenetration", 0.0f,
|
||||||
0.0f, std::numeric_limits<float>::max(), Game::DVAR_CODINFO,
|
0.0f, std::numeric_limits<float>::max(), Game::DVAR_CODINFO, "Set to a value greater than 0 to override the surface penetration depth");
|
||||||
"Set to a value greater than 0 to override the surface penetration depth");
|
|
||||||
BGBulletRange = Game::Dvar_RegisterFloat("bg_bulletRange", 8192.0f,
|
BGBulletRange = Game::Dvar_RegisterFloat("bg_bulletRange", 8192.0f,
|
||||||
0.0f, std::numeric_limits<float>::max(), Game::DVAR_CODINFO,
|
0.0f, std::numeric_limits<float>::max(), Game::DVAR_CODINFO, "Max range used when calculating the bullet end position");
|
||||||
"Max range used when calculating the bullet end position");
|
|
||||||
|
|
||||||
Utils::Hook(0x4F6980, BG_GetSurfacePenetrationDepthStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x4F6980, BG_GetSurfacePenetrationDepthStub, HOOK_JUMP).install()->quick();
|
||||||
Utils::Hook(0x440340, Bullet_FireStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x440340, Bullet_FireStub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
|
Utils::Hook(0x440368, BG_srand_Hk, HOOK_CALL).install()->quick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,5 +15,7 @@ namespace Components
|
|||||||
static float BG_GetSurfacePenetrationDepthStub(const Game::WeaponDef* weapDef, int surfaceType);
|
static float BG_GetSurfacePenetrationDepthStub(const Game::WeaponDef* weapDef, int surfaceType);
|
||||||
|
|
||||||
static void Bullet_FireStub();
|
static void Bullet_FireStub();
|
||||||
|
|
||||||
|
static void BG_srand_Hk(unsigned int* pHoldrand);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,128 +0,0 @@
|
|||||||
#include <STDInclude.hpp>
|
|
||||||
|
|
||||||
namespace Components
|
|
||||||
{
|
|
||||||
void FrameTime::NetSleep(int mSec)
|
|
||||||
{
|
|
||||||
if (mSec < 0) mSec = 0;
|
|
||||||
|
|
||||||
fd_set fdr;
|
|
||||||
FD_ZERO(&fdr);
|
|
||||||
|
|
||||||
auto highestfd = INVALID_SOCKET;
|
|
||||||
if (*Game::ip_socket != INVALID_SOCKET)
|
|
||||||
{
|
|
||||||
FD_SET(*Game::ip_socket, &fdr);
|
|
||||||
highestfd = *Game::ip_socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (highestfd == INVALID_SOCKET)
|
|
||||||
{
|
|
||||||
// windows ain't happy when select is called without valid FDs
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(mSec));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
timeval timeout;
|
|
||||||
timeout.tv_sec = mSec / 1000;
|
|
||||||
timeout.tv_usec = (mSec % 1000) * 1000;
|
|
||||||
|
|
||||||
const auto retVal = select(highestfd + 1, &fdr, nullptr, nullptr, &timeout);
|
|
||||||
|
|
||||||
if (retVal == SOCKET_ERROR)
|
|
||||||
{
|
|
||||||
Logger::Warning(Game::CON_CHANNEL_SYSTEM, "Select() syscall failed: {}\n", Game::NET_ErrorString());
|
|
||||||
}
|
|
||||||
else if (retVal > 0)
|
|
||||||
{
|
|
||||||
if ((*Game::com_sv_running)->current.enabled)
|
|
||||||
{
|
|
||||||
Game::Com_ServerPacketEvent();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Game::Com_ClientPacketEvent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FrameTime::SVFrameWaitFunc()
|
|
||||||
{
|
|
||||||
int sv_residualTime = *reinterpret_cast<int*>(0x2089E14);
|
|
||||||
|
|
||||||
if (sv_residualTime < 50)
|
|
||||||
{
|
|
||||||
NetSleep(50 - sv_residualTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void __declspec(naked) FrameTime::SVFrameWaitStub()
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
pushad
|
|
||||||
call SVFrameWaitFunc
|
|
||||||
popad
|
|
||||||
|
|
||||||
push 4CD420h
|
|
||||||
retn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int FrameTime::ComTimeVal(int minMsec)
|
|
||||||
{
|
|
||||||
const auto timeVal = Game::Sys_Milliseconds() - *Game::com_frameTime;
|
|
||||||
return (timeVal >= minMsec ? 0 : minMsec - timeVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
int FrameTime::ComFrameWait(int minMsec)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
const auto timeVal = ComTimeVal(minMsec);
|
|
||||||
NetSleep(timeVal < 1 ? 0 : timeVal - 1);
|
|
||||||
} while (ComTimeVal(minMsec));
|
|
||||||
|
|
||||||
const auto lastTime = *Game::com_frameTime;
|
|
||||||
Game::Com_EventLoop();
|
|
||||||
*Game::com_frameTime = Game::Sys_Milliseconds();
|
|
||||||
|
|
||||||
return *Game::com_frameTime - lastTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
void __declspec(naked) FrameTime::ComFrameWaitStub()
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
push ecx
|
|
||||||
pushad
|
|
||||||
|
|
||||||
push edi
|
|
||||||
call ComFrameWait
|
|
||||||
add esp, 4
|
|
||||||
|
|
||||||
mov [esp + 20h], eax
|
|
||||||
popad
|
|
||||||
pop eax
|
|
||||||
mov ecx, eax
|
|
||||||
|
|
||||||
mov edx, 1AD7934h // com_sv_running
|
|
||||||
cmp byte ptr [edx + 10h], 0
|
|
||||||
|
|
||||||
push 47DDC1h
|
|
||||||
retn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FrameTime::FrameTime()
|
|
||||||
{
|
|
||||||
if (Dedicated::IsEnabled())
|
|
||||||
{
|
|
||||||
Utils::Hook(0x4BAAAD, SVFrameWaitStub, HOOK_CALL).install()->quick();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Utils::Hook(0x47DD80, ComFrameWaitStub, HOOK_JUMP).install()->quick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace Components
|
|
||||||
{
|
|
||||||
class FrameTime : public Component
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FrameTime();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static void SVFrameWaitStub();
|
|
||||||
static void SVFrameWaitFunc();
|
|
||||||
|
|
||||||
static void NetSleep(int mSec);
|
|
||||||
|
|
||||||
static int ComTimeVal(int minMsec);
|
|
||||||
static int ComFrameWait(int minMsec);
|
|
||||||
static void ComFrameWaitStub();
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
#include <STDInclude.hpp>
|
|
||||||
|
|
||||||
namespace Components
|
|
||||||
{
|
|
||||||
std::unordered_map<std::string, const char*> MusicalTalent::SoundAliasList;
|
|
||||||
|
|
||||||
void MusicalTalent::Replace(const std::string& sound, const char* file)
|
|
||||||
{
|
|
||||||
MusicalTalent::SoundAliasList[Utils::String::ToLower(sound)] = file;
|
|
||||||
}
|
|
||||||
|
|
||||||
Game::XAssetHeader MusicalTalent::ModifyAliases(Game::XAssetType type, const std::string& filename)
|
|
||||||
{
|
|
||||||
Game::XAssetHeader header = { nullptr };
|
|
||||||
|
|
||||||
if (MusicalTalent::SoundAliasList.find(Utils::String::ToLower(filename)) != MusicalTalent::SoundAliasList.end())
|
|
||||||
{
|
|
||||||
Game::snd_alias_list_t* aliases = Game::DB_FindXAssetHeader(type, filename.data()).sound;
|
|
||||||
|
|
||||||
if (aliases && aliases->count > 0 && aliases->head && aliases->head->soundFile)
|
|
||||||
{
|
|
||||||
if (aliases->head->soundFile->type == Game::snd_alias_type_t::SAT_STREAMED)
|
|
||||||
{
|
|
||||||
aliases->head->soundFile->u.streamSnd.filename.info.raw.name = MusicalTalent::SoundAliasList[Utils::String::ToLower(filename)];
|
|
||||||
}
|
|
||||||
|
|
||||||
header.sound = aliases;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return header;
|
|
||||||
}
|
|
||||||
|
|
||||||
MusicalTalent::MusicalTalent()
|
|
||||||
{
|
|
||||||
if (ZoneBuilder::IsEnabled() || Dedicated::IsEnabled()) return;
|
|
||||||
|
|
||||||
AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_SOUND, MusicalTalent::ModifyAliases);
|
|
||||||
|
|
||||||
MusicalTalent::Replace("music_mainmenu_mp", "hz_t_menumusic.mp3");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace Components
|
|
||||||
{
|
|
||||||
class MusicalTalent : public Component
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MusicalTalent();
|
|
||||||
|
|
||||||
static void Replace(const std::string& sound, const char* file);
|
|
||||||
|
|
||||||
private:
|
|
||||||
static std::unordered_map<std::string, const char*> SoundAliasList;
|
|
||||||
static Game::XAssetHeader ModifyAliases(Game::XAssetType type, const std::string& filename);
|
|
||||||
};
|
|
||||||
}
|
|
@ -396,7 +396,7 @@ namespace Components
|
|||||||
info.set("wwwDownload", (Dvar::Var("sv_wwwDownload").get<bool>() ? "1" : "0"));
|
info.set("wwwDownload", (Dvar::Var("sv_wwwDownload").get<bool>() ? "1" : "0"));
|
||||||
info.set("wwwUrl", Dvar::Var("sv_wwwBaseUrl").get<std::string>());
|
info.set("wwwUrl", Dvar::Var("sv_wwwBaseUrl").get<std::string>());
|
||||||
|
|
||||||
Network::SendCommand(address, "infoResponse", info.build());
|
Network::SendCommand(address, "infoResponse", "\\" + info.build());
|
||||||
});
|
});
|
||||||
|
|
||||||
Network::OnClientPacket("infoResponse", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
Network::OnClientPacket("infoResponse", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
|
@ -332,6 +332,9 @@ namespace Components
|
|||||||
// Fix crash as nullptr goes unchecked
|
// Fix crash as nullptr goes unchecked
|
||||||
Utils::Hook(0x437CAD, QuickPatch::SND_GetAliasOffset_Stub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x437CAD, QuickPatch::SND_GetAliasOffset_Stub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
|
// Make VA thread safe
|
||||||
|
Utils::Hook(0x4785B0, Utils::String::VA, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
// protocol version (workaround for hacks)
|
// protocol version (workaround for hacks)
|
||||||
Utils::Hook::Set<int>(0x4FB501, PROTOCOL);
|
Utils::Hook::Set<int>(0x4FB501, PROTOCOL);
|
||||||
|
|
||||||
|
@ -200,7 +200,7 @@ namespace Components
|
|||||||
Utils::InfoString info = ServerInfo::GetInfo();
|
Utils::InfoString info = ServerInfo::GetInfo();
|
||||||
info.set("challenge", Utils::ParseChallenge(data));
|
info.set("challenge", Utils::ParseChallenge(data));
|
||||||
|
|
||||||
for (auto i = 0; i < atoi(info.get("sv_maxclients").data()); ++i) // Maybe choose 18 here?
|
for (std::size_t i = 0; i < Game::MAX_CLIENTS; ++i)
|
||||||
{
|
{
|
||||||
auto score = 0;
|
auto score = 0;
|
||||||
auto ping = 0;
|
auto ping = 0;
|
||||||
@ -210,7 +210,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
if (Game::svs_clients[i].header.state < Game::CS_CONNECTED) continue;
|
if (Game::svs_clients[i].header.state < Game::CS_CONNECTED) continue;
|
||||||
|
|
||||||
score = Game::SV_GameClientNum_Score(i);
|
score = Game::SV_GameClientNum_Score(static_cast<int>(i));
|
||||||
ping = Game::svs_clients[i].ping;
|
ping = Game::svs_clients[i].ping;
|
||||||
name = Game::svs_clients[i].name;
|
name = Game::svs_clients[i].name;
|
||||||
}
|
}
|
||||||
@ -226,7 +226,7 @@ namespace Components
|
|||||||
playerList.append(Utils::String::VA("%i %i \"%s\"\n", score, ping, name.data()));
|
playerList.append(Utils::String::VA("%i %i \"%s\"\n", score, ping, name.data()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::SendCommand(address, "statusResponse", info.build() + "\n" + playerList + "\n");
|
Network::SendCommand(address, "statusResponse", "\\" + info.build() + "\n" + playerList + "\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
Network::OnClientPacket("statusResponse", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
Network::OnClientPacket("statusResponse", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
#include <STDInclude.hpp>
|
|
||||||
|
|
||||||
namespace Components
|
|
||||||
{
|
|
||||||
__declspec(naked) void Threading::FrameEpilogueStub()
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
pop edi
|
|
||||||
pop esi
|
|
||||||
pop ebx
|
|
||||||
mov esp, ebp
|
|
||||||
pop ebp
|
|
||||||
retn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
__declspec(naked) void Threading::PacketEventStub()
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
mov eax, 49F0B0h
|
|
||||||
call eax
|
|
||||||
mov eax, 458160h
|
|
||||||
jmp eax
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Threading::Threading()
|
|
||||||
{
|
|
||||||
// remove starting of server thread from Com_Init_Try_Block_Function
|
|
||||||
Utils::Hook::Nop(0x60BEC0, 5);
|
|
||||||
|
|
||||||
// make server thread function jump to per-frame stuff
|
|
||||||
Utils::Hook(0x627049, 0x6271CE, HOOK_JUMP).install()->quick();
|
|
||||||
|
|
||||||
// make SV_WaitServer insta-return
|
|
||||||
Utils::Hook::Set<BYTE>(0x4256F0, 0xC3);
|
|
||||||
|
|
||||||
// dvar setting function, unknown stuff related to server thread sync
|
|
||||||
Utils::Hook::Set<BYTE>(0x647781, 0xEB);
|
|
||||||
|
|
||||||
Utils::Hook(0x627695, 0x627040, HOOK_CALL).install()->quick();
|
|
||||||
Utils::Hook(0x43D1C7, Threading::PacketEventStub, HOOK_JUMP).install()->quick();
|
|
||||||
Utils::Hook(0x6272E3, Threading::FrameEpilogueStub, HOOK_JUMP).install()->quick();
|
|
||||||
|
|
||||||
// Make VA thread safe
|
|
||||||
Utils::Hook(0x4785B0, Utils::String::VA, HOOK_JUMP).install()->quick();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace Components
|
|
||||||
{
|
|
||||||
class Threading : public Component
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Threading();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static void FrameEpilogueStub();
|
|
||||||
static void PacketEventStub();
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,4 +1,5 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "Game/Engine/LargeLocal.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -39,8 +40,8 @@ namespace Components
|
|||||||
Game::msg_t msg{};
|
Game::msg_t msg{};
|
||||||
const auto clientNum = client - Game::svs_clients;
|
const auto clientNum = client - Game::svs_clients;
|
||||||
|
|
||||||
const auto msg_buf_large = std::make_unique<unsigned char[]>(0x10000);
|
const Game::Engine::LargeLocal msg_buf_large_local(0x10000);
|
||||||
auto* msg_buf = msg_buf_large.get();
|
auto* msg_buf = static_cast<unsigned char*>(msg_buf_large_local.GetBuf());
|
||||||
|
|
||||||
assert(VoicePacketCount[clientNum] >= 0);
|
assert(VoicePacketCount[clientNum] >= 0);
|
||||||
|
|
||||||
|
@ -51,6 +51,8 @@ BOOL APIENTRY DllMain(HINSTANCE /*hinstDLL*/, DWORD fdwReason, LPVOID /*lpvReser
|
|||||||
SetProcessDEPPolicy(PROCESS_DEP_ENABLE);
|
SetProcessDEPPolicy(PROCESS_DEP_ENABLE);
|
||||||
Steam::Proxy::RunMod();
|
Steam::Proxy::RunMod();
|
||||||
|
|
||||||
|
std::srand(std::uint32_t(std::time(nullptr)) ^ ~(GetTickCount() * GetCurrentProcessId()));
|
||||||
|
|
||||||
#ifndef DISABLE_BINARY_CHECK
|
#ifndef DISABLE_BINARY_CHECK
|
||||||
// Ensure we're working with our desired binary
|
// Ensure we're working with our desired binary
|
||||||
auto* _module = reinterpret_cast<char*>(0x400000);
|
auto* _module = reinterpret_cast<char*>(0x400000);
|
||||||
|
Loading…
Reference in New Issue
Block a user