mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
Improve code and readability
Additional cleanup and micro refactor of the console. New signature for S2+ SQVM's. Every release of the game executable is now supported
This commit is contained in:
parent
f56acb1a42
commit
683dcfe534
@ -5,11 +5,12 @@
|
|||||||
|
|
||||||
#include <detours.h>
|
#include <detours.h>
|
||||||
|
|
||||||
|
#include "hooks.h"
|
||||||
#include "patterns.h"
|
#include "patterns.h"
|
||||||
|
|
||||||
DWORD __stdcall ProcessConsoleWorker(LPVOID);
|
//---------------------------------------------------------------------------------
|
||||||
FILE* sBuildTxt;
|
// Console Hooks
|
||||||
CHAR sBuildBuf[1024];
|
//---------------------------------------------------------------------------------
|
||||||
|
|
||||||
void SetupConsole()
|
void SetupConsole()
|
||||||
{
|
{
|
||||||
@ -21,6 +22,8 @@ void SetupConsole()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set the window title
|
// Set the window title
|
||||||
|
FILE* sBuildTxt;
|
||||||
|
CHAR sBuildBuf[1024];
|
||||||
#pragma warning(suppress : 4996)
|
#pragma warning(suppress : 4996)
|
||||||
sBuildTxt = fopen("build.txt", "r");
|
sBuildTxt = fopen("build.txt", "r");
|
||||||
if (sBuildTxt)
|
if (sBuildTxt)
|
||||||
@ -37,6 +40,7 @@ void SetupConsole()
|
|||||||
freopen_s(&fDummy, "CONOUT$", "w", stderr);
|
freopen_s(&fDummy, "CONOUT$", "w", stderr);
|
||||||
|
|
||||||
// Create a worker thread to process console commands.
|
// Create a worker thread to process console commands.
|
||||||
|
DWORD __stdcall ProcessConsoleWorker(LPVOID);
|
||||||
HANDLE hThread = CreateThread(NULL, NULL, ProcessConsoleWorker, NULL, NULL, NULL);
|
HANDLE hThread = CreateThread(NULL, NULL, ProcessConsoleWorker, NULL, NULL, NULL);
|
||||||
CloseHandle(hThread);
|
CloseHandle(hThread);
|
||||||
}
|
}
|
||||||
@ -44,16 +48,16 @@ void SetupConsole()
|
|||||||
bool Hook_Cvar_IsFlagSet(int ** cvar, int flag)
|
bool Hook_Cvar_IsFlagSet(int ** cvar, int flag)
|
||||||
{
|
{
|
||||||
int real_flags = *(*(cvar + (72/(sizeof(void*)))) + (56/sizeof(int)));
|
int real_flags = *(*(cvar + (72/(sizeof(void*)))) + (56/sizeof(int)));
|
||||||
#ifdef _DEBUG
|
|
||||||
printf("----------------------------------------------\n");
|
printf("----------------------------------------------\n");
|
||||||
printf("Real flags: %08X\n", real_flags);
|
printf("Real flags: %08X\n", real_flags);
|
||||||
#endif
|
|
||||||
// mask off FCVAR_CHEATS and FCVAR_DEVELOPMENTONLY
|
// Mask off FCVAR_CHEATS and FCVAR_DEVELOPMENTONLY
|
||||||
real_flags &= 0xFFFFBFFD;
|
real_flags &= 0xFFFFBFFD;
|
||||||
#ifdef _DEBUG
|
|
||||||
printf(" masked: %08X\n", real_flags);
|
printf(" Masked: %08X\n", real_flags);
|
||||||
printf(" checking: %08X\n", flag);
|
printf(" Checking: %08X\n", flag);
|
||||||
#endif
|
|
||||||
if (flag & 0x80000)
|
if (flag & 0x80000)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@ -62,48 +66,40 @@ bool Hook_Cvar_IsFlagSet(int ** cvar, int flag)
|
|||||||
return (real_flags & flag) != 0;
|
return (real_flags & flag) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool g_dev = false;
|
//---------------------------------------------------------------------------------
|
||||||
void ToggleDevCommands()
|
// Console Worker
|
||||||
{
|
//---------------------------------------------------------------------------------
|
||||||
DetourTransactionBegin();
|
|
||||||
DetourUpdateThread(GetCurrentThread());
|
|
||||||
|
|
||||||
if (!g_dev)
|
|
||||||
DetourAttach((PVOID*)&Cvar_IsFlagSet, &Hook_Cvar_IsFlagSet);
|
|
||||||
else
|
|
||||||
DetourDetach((PVOID*)&Cvar_IsFlagSet, &Hook_Cvar_IsFlagSet);
|
|
||||||
|
|
||||||
if (DetourTransactionCommit() != NO_ERROR)
|
|
||||||
TerminateProcess(GetCurrentProcess(), 0xBAD0C0DE);
|
|
||||||
|
|
||||||
g_dev = ~g_dev;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD __stdcall ProcessConsoleWorker(LPVOID)
|
DWORD __stdcall ProcessConsoleWorker(LPVOID)
|
||||||
{
|
{
|
||||||
|
// Loop forever
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
std::string sCommand;
|
||||||
|
|
||||||
// Loop forever.
|
// Get the user input on the debug console
|
||||||
while (true)
|
printf(">");
|
||||||
{
|
std::getline(std::cin, sCommand);
|
||||||
std::string sCommand;
|
|
||||||
|
|
||||||
// get the user input on the debug console
|
if (sCommand == "toggle dev")
|
||||||
printf(">");
|
{
|
||||||
std::getline(std::cin, sCommand);
|
ToggleDevCommands();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (sCommand == "toggle dev")
|
if (sCommand == "toggle net")
|
||||||
{
|
{
|
||||||
ToggleDevCommands();
|
ToggleNetHooks();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute the command in the r5 SQVM
|
// Execute the command in the r5 SQVM
|
||||||
CommandExecute(NULL, sCommand.c_str());
|
CommandExecute(NULL, sCommand.c_str());
|
||||||
sCommand.clear();
|
sCommand.clear();
|
||||||
|
|
||||||
// Sleep and loop.
|
// Sleep and loop
|
||||||
Sleep(50);
|
Sleep(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
@ -11,9 +11,9 @@ __declspec(dllexport) void DummyExport()
|
|||||||
// Required for detours.
|
// Required for detours.
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//---------------------------------------------------------------------------------
|
||||||
// Main
|
// Main
|
||||||
//
|
//---------------------------------------------------------------------------------
|
||||||
|
|
||||||
void InitializeR5Dev()
|
void InitializeR5Dev()
|
||||||
{
|
{
|
||||||
@ -34,12 +34,16 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved)
|
|||||||
switch (dwReason)
|
switch (dwReason)
|
||||||
{
|
{
|
||||||
case DLL_PROCESS_ATTACH:
|
case DLL_PROCESS_ATTACH:
|
||||||
|
{
|
||||||
InitializeR5Dev();
|
InitializeR5Dev();
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case DLL_PROCESS_DETACH:
|
case DLL_PROCESS_DETACH:
|
||||||
|
{
|
||||||
TerminateR5Dev();
|
TerminateR5Dev();
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
@ -8,9 +8,9 @@
|
|||||||
#include "hooks.h"
|
#include "hooks.h"
|
||||||
#include "patterns.h"
|
#include "patterns.h"
|
||||||
|
|
||||||
//
|
//---------------------------------------------------------------------------------
|
||||||
// Engine Hooks
|
// Engine Hooks
|
||||||
//
|
//---------------------------------------------------------------------------------
|
||||||
|
|
||||||
struct __declspec(align(8)) netpacket_t
|
struct __declspec(align(8)) netpacket_t
|
||||||
{
|
{
|
||||||
@ -76,24 +76,28 @@ bool Hook_SQVM_LoadScript(void* sqvm, const char* script_path, const char* scrip
|
|||||||
char filepath[MAX_PATH] = {0};
|
char filepath[MAX_PATH] = {0};
|
||||||
sprintf_s(filepath, MAX_PATH, "platform\\%s", script_path);
|
sprintf_s(filepath, MAX_PATH, "platform\\%s", script_path);
|
||||||
|
|
||||||
// flip forward slashes in filepath to windows-style backslash
|
// Flip forward slashes in filepath to windows-style backslash
|
||||||
for (int i = 0; i < strlen(filepath); i++)
|
for (int i = 0; i < strlen(filepath); i++)
|
||||||
|
{
|
||||||
if (filepath[i] == '/')
|
if (filepath[i] == '/')
|
||||||
|
{
|
||||||
filepath[i] = '\\';
|
filepath[i] = '\\';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
printf(" + Loading SQVM Script '%s' ...\n", filepath);
|
printf(" + Loading SQVM Script '%s' ...\n", filepath);
|
||||||
if (FileExists(filepath) && SQVM_LoadScript(sqvm, filepath, script_name, flag))
|
if (FileExists(filepath) && SQVM_LoadScript(sqvm, filepath, script_name, flag))
|
||||||
{
|
{
|
||||||
return true; // redirect to disk worked / script exists on disk..
|
return true; // Redirect to disk worked / script exists on disk..
|
||||||
}
|
}
|
||||||
|
|
||||||
printf(" |- FAILED, loading normally / from VPK...\n");
|
printf(" |- FAILED, loading from SearchPath / VPK...\n");
|
||||||
return SQVM_LoadScript(sqvm, script_path, script_name, flag);
|
return SQVM_LoadScript(sqvm, script_path, script_name, flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//---------------------------------------------------------------------------------
|
||||||
// Hook Management
|
// Hook Management
|
||||||
//
|
//---------------------------------------------------------------------------------
|
||||||
|
|
||||||
void InstallHooks()
|
void InstallHooks()
|
||||||
{
|
{
|
||||||
@ -102,12 +106,8 @@ void InstallHooks()
|
|||||||
DetourUpdateThread(GetCurrentThread());
|
DetourUpdateThread(GetCurrentThread());
|
||||||
|
|
||||||
// Hook Functions
|
// Hook Functions
|
||||||
DetourAttach(&SQVM_Print, &Hook_SQVM_Print);
|
DetourAttach((LPVOID*)&SQVM_Print, &Hook_SQVM_Print);
|
||||||
DetourAttach((PVOID*)&SQVM_LoadScript, &Hook_SQVM_LoadScript);
|
DetourAttach((LPVOID*)&SQVM_LoadScript, &Hook_SQVM_LoadScript);
|
||||||
|
|
||||||
// TODO these might be fucked right now so they're disabled
|
|
||||||
//DetourAttach((PVOID*)&NET_SendDatagram, &Hook_NET_SendDatagram);
|
|
||||||
//DetourAttach((PVOID*)&NET_ReceiveDatagram, &Hook_NET_ReceiveDatagram);
|
|
||||||
|
|
||||||
// Commit the transaction
|
// Commit the transaction
|
||||||
if (DetourTransactionCommit() != NO_ERROR)
|
if (DetourTransactionCommit() != NO_ERROR)
|
||||||
@ -119,11 +119,62 @@ void InstallHooks()
|
|||||||
|
|
||||||
void RemoveHooks()
|
void RemoveHooks()
|
||||||
{
|
{
|
||||||
// Begin the detour transaction, to unhook the the process
|
|
||||||
DetourTransactionBegin();
|
DetourTransactionBegin();
|
||||||
DetourUpdateThread(GetCurrentThread());
|
DetourUpdateThread(GetCurrentThread());
|
||||||
|
|
||||||
//DetourDetach(&SQVMPrint, &Hook_SQVMPrint);
|
// Unhook Functions
|
||||||
|
DetourDetach((LPVOID*)&SQVM_Print, &Hook_SQVM_Print);
|
||||||
|
DetourDetach((LPVOID*)&SQVM_LoadScript, &Hook_SQVM_LoadScript);
|
||||||
|
|
||||||
DetourTransactionCommit();
|
DetourTransactionCommit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ToggleDevCommands()
|
||||||
|
{
|
||||||
|
bool g_dev = false;
|
||||||
|
|
||||||
|
DetourTransactionBegin();
|
||||||
|
DetourUpdateThread(GetCurrentThread());
|
||||||
|
|
||||||
|
if (!g_dev)
|
||||||
|
{
|
||||||
|
DetourAttach((PVOID*)&Cvar_IsFlagSet, &Hook_Cvar_IsFlagSet);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DetourDetach((PVOID*)&Cvar_IsFlagSet, &Hook_Cvar_IsFlagSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DetourTransactionCommit() != NO_ERROR)
|
||||||
|
{
|
||||||
|
TerminateProcess(GetCurrentProcess(), 0xBAD0C0DE);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_dev = ~g_dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ToggleNetHooks()
|
||||||
|
{
|
||||||
|
bool g_net = false;
|
||||||
|
|
||||||
|
DetourTransactionBegin();
|
||||||
|
DetourUpdateThread(GetCurrentThread());
|
||||||
|
|
||||||
|
if (!g_net)
|
||||||
|
{
|
||||||
|
DetourAttach((PVOID*)&NET_SendDatagram, &Hook_NET_SendDatagram);
|
||||||
|
DetourAttach((PVOID*)&NET_ReceiveDatagram, &Hook_NET_ReceiveDatagram);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DetourDetach((PVOID*)&NET_SendDatagram, &Hook_NET_SendDatagram);
|
||||||
|
DetourDetach((PVOID*)&NET_ReceiveDatagram, &Hook_NET_ReceiveDatagram);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DetourTransactionCommit() != NO_ERROR)
|
||||||
|
{
|
||||||
|
TerminateProcess(GetCurrentProcess(), 0xBAD0C0DE);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_net = ~g_net;
|
||||||
}
|
}
|
@ -1,4 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
void InstallHooks();
|
void InstallHooks();
|
||||||
void RemoveHooks();
|
void RemoveHooks();
|
||||||
|
void ToggleDevCommands();
|
||||||
|
void ToggleNetHooks();
|
||||||
|
|
||||||
|
bool Hook_Cvar_IsFlagSet(int** cvar, int flag);
|
@ -7,28 +7,23 @@ namespace
|
|||||||
SigScan fScanner;
|
SigScan fScanner;
|
||||||
|
|
||||||
LONGLONG hGameConsole = fScanner.FindPattern("r5apex.exe", "\x48\x89\x5C\x24\x08\x57\x48\x83\xEC\x20\x48\x8D\x0D\x27\x61\xa5\x1e\x41\x8B\xD8", "xxxx?xxxxxxxx????xxx");
|
LONGLONG hGameConsole = fScanner.FindPattern("r5apex.exe", "\x48\x89\x5C\x24\x08\x57\x48\x83\xEC\x20\x48\x8D\x0D\x27\x61\xa5\x1e\x41\x8B\xD8", "xxxx?xxxxxxxx????xxx");
|
||||||
void (*CommandExecute)(void* self, const char* cmd) = (void (*)(void*, const char*))hGameConsole; //0x140244900
|
void (*CommandExecute)(void* self, const char* cmd) = (void (*)(void*, const char*))hGameConsole;
|
||||||
/*48 89 5C 24 ? 57 48 83 EC 20 48 8D 0D ? ? ? ? 41 8B D8*/
|
|
||||||
|
|
||||||
LONGLONG hGameConsoleFlag = fScanner.FindPattern("r5apex.exe", "\x48\x8B\x41\x48\x85\x50\x38", "xxxxxxx");
|
LONGLONG hGameConsoleFlag = fScanner.FindPattern("r5apex.exe", "\x48\x8B\x41\x48\x85\x50\x38", "xxxxxxx");
|
||||||
bool (*Cvar_IsFlagSet)(int** cvar, int flag) = (bool (*)(int**, int))hGameConsoleFlag; //0x1404C87C0
|
bool (*Cvar_IsFlagSet)(int** cvar, int flag) = (bool (*)(int**, int))hGameConsoleFlag;
|
||||||
/*48 8B 41 48 85 50 38*/
|
|
||||||
|
|
||||||
LONGLONG hSquirrelVMPrint = fScanner.FindPattern("r5apex.exe", "\x48\x8B\xC4\x48\x89\x50\x10\x4C\x89\x40\x18\x4C\x89\x48\x20\x53\x56\x57\x48\x81\xEC\x30\x08\x00\x00\x48\x8B\xDA\x48\x8D\x70\x18\x48\x8B\xF9\xE8\x00\x00\x00\xff\x48\x89\x74\x24\x28\x48\x8d\x54\x24\x30\x33", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx???xxxxxxxxxxxx");
|
LONGLONG hSquirrelVMPrint = fScanner.FindPattern("r5apex.exe", "\x48\x8B\xC4\x48\x89\x50\x10\x4C\x89\x40\x18\x4C\x89\x48\x20\x53\x56\x57\x48\x81\xEC\x30\x08\x00\x00\x48\x8B\xDA\x48\x8D\x70\x18\x48\x8B\xF9\xE8\x00\x00\x00\xff\x48\x89\x74\x24\x28\x48\x8d\x54\x24\x30\x33", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx???xxxxxxxxxxxx");
|
||||||
void* SQVM_Print = (void*)hSquirrelVMPrint; //0x1410A4330
|
void* SQVM_Print = (void*)hSquirrelVMPrint;
|
||||||
/*48 8B C4 48 89 50 10 4C 89 40 18 4C 89 48 20 53 56 57 48 81 EC ? ? ? ? 48 8B DA 48 8D 70 18 48 8B F9 E8 ? ? ? ? 48 89 74 24 ? 48 8D 54 24 ? 33 F6 4C 8B CB 41 B8 ? ? ? ? 48 89 74 24 ? 48 8B 08 48 83 C9 01 E8 ? ? ? ? 85 C0 B9 ? ? ? ? 0F 48 C1 0F B6 8C 24 ? ? ? ? 3D ? ? ? ? 48 8B 47 50*/
|
|
||||||
|
|
||||||
LONGLONG hSquirrelVMScript = fScanner.FindPattern("r5apex.exe", "\x48\x89\x5C\x24\x10\x48\x89\x74\x24\x18\x48\x89\x7C\x24\x20\x48\x89\x4C\x24\x08\x55\x41\x54\x41\x55\x41\x56\x41\x57\x48\x8D\x6C", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
|
LONGLONG hSquirrelVMScript = fScanner.FindPattern("r5apex.exe", "\x48\x89\x5C\x24\x10\x48\x89\x74\x24\x18\x48\x89\x7C\x24\x20\x48\x89\x4C\x24\x08\x55\x41\x54\x41\x55\x41\x56\x41\x57\x48\x8D\x6C", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); // Uncomment for S0 and S1
|
||||||
bool (*SQVM_LoadScript)(void* sqvm, const char* script_path, const char* script_name, int flag) = (bool (*)(void*, const char*, const char*, int))hSquirrelVMScript; //0x1410A1510
|
//LONGLONG hSquirrelVMScript = fScanner.FindPattern("r5apex.exe", "\x48\x8B\xC4\x48\x89\x48\x08\x55\x41\x56\x48\x8D\x68", "xxxxxxxxxxxxx"); // Uncomment for anything S2 and above (current S8)
|
||||||
/*E8 ? ? ? ? 84 C0 74 1C 41 B9 ? ? ? ?*/
|
bool (*SQVM_LoadScript)(void* sqvm, const char* script_path, const char* script_name, int flag) = (bool (*)(void*, const char*, const char*, int))hSquirrelVMScript;
|
||||||
|
|
||||||
LONGLONG hNetRXDatagram = fScanner.FindPattern("r5apex.exe", "\x48\x89\x74\x24\x18\x48\x89\x7C\x24\x20\x55\x41\x54\x41\x55\x41\x56\x41\x57\x48\x8D\xAC\x24\x50\xeb", "xxxxxxxxxxxxxxxxxxxxxxxxx");
|
LONGLONG hNetRXDatagram = fScanner.FindPattern("r5apex.exe", "\x48\x89\x74\x24\x18\x48\x89\x7C\x24\x20\x55\x41\x54\x41\x55\x41\x56\x41\x57\x48\x8D\xAC\x24\x50\xeb", "xxxxxxxxxxxxxxxxxxxxxxxxx");
|
||||||
bool (*NET_ReceiveDatagram)(int, void*, bool) = (bool (*)(int, void*, bool))hNetRXDatagram; //0x1402B46B0
|
bool (*NET_ReceiveDatagram)(int, void*, bool) = (bool (*)(int, void*, bool))hNetRXDatagram;
|
||||||
/*E8 ? ? ? ? 84 C0 75 35 48 8B D3*/
|
|
||||||
|
|
||||||
LONGLONG hNetTXDatagram = fScanner.FindPattern("r5apex.exe", "\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x10\x48\x89\x74\x24\x18\x57\x48\x83\xEC\x20\x48\x8B\xF9\x41\xb8\x2d", "xxxxxxxxxxxxxxxxxxxxxxxxxx");
|
LONGLONG hNetTXDatagram = fScanner.FindPattern("r5apex.exe", "\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x10\x48\x89\x74\x24\x18\x57\x48\x83\xEC\x20\x48\x8B\xF9\x41\xb8\x2d", "xxxxxxxxxxxxxxxxxxxxxxxxxx");
|
||||||
unsigned int (*NET_SendDatagram)(SOCKET s, const char* ptxt, int len, void* netchan_maybe, bool raw) = (unsigned int (*)(SOCKET, const char*, int, void*, bool))hNetTXDatagram; //0x1402B2C90
|
unsigned int (*NET_SendDatagram)(SOCKET s, const char* ptxt, int len, void* netchan_maybe, bool raw) = (unsigned int (*)(SOCKET, const char*, int, void*, bool))hNetTXDatagram;
|
||||||
/*E8 ? ? ? ? 40 88 6B 18*/
|
|
||||||
|
|
||||||
void PrintHAddress() // Test the sigscan results
|
void PrintHAddress() // Test the sigscan results
|
||||||
{
|
{
|
||||||
|
@ -10,7 +10,9 @@ public:
|
|||||||
MODULEINFO modinfo = { 0 };
|
MODULEINFO modinfo = { 0 };
|
||||||
HMODULE hModule = GetModuleHandle(szModule);
|
HMODULE hModule = GetModuleHandle(szModule);
|
||||||
if (hModule == 0)
|
if (hModule == 0)
|
||||||
|
{
|
||||||
return modinfo;
|
return modinfo;
|
||||||
|
}
|
||||||
GetModuleInformation(GetCurrentProcess(), hModule, &modinfo, sizeof(MODULEINFO));
|
GetModuleInformation(GetCurrentProcess(), hModule, &modinfo, sizeof(MODULEINFO));
|
||||||
return modinfo;
|
return modinfo;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user