[QuickPatch] Make startsingleplayer command work with iw4x-sp
This commit is contained in:
parent
0fde17bbb2
commit
fe73bb9827
@ -138,6 +138,22 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::filesystem::path FileSystem::GetAppdataPath()
|
||||||
|
{
|
||||||
|
PWSTR path;
|
||||||
|
if (!SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &path)))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to read APPDATA path!");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto _0 = gsl::finally([&path]
|
||||||
|
{
|
||||||
|
CoTaskMemFree(path);
|
||||||
|
});
|
||||||
|
|
||||||
|
return std::filesystem::path(path) / "xlabs";
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> FileSystem::GetFileList(const std::string& path, const std::string& extension)
|
std::vector<std::string> FileSystem::GetFileList(const std::string& path, const std::string& extension)
|
||||||
{
|
{
|
||||||
std::vector<std::string> fileList;
|
std::vector<std::string> fileList;
|
||||||
|
@ -89,6 +89,7 @@ namespace Components
|
|||||||
FileSystem();
|
FileSystem();
|
||||||
~FileSystem();
|
~FileSystem();
|
||||||
|
|
||||||
|
static std::filesystem::path GetAppdataPath();
|
||||||
static std::vector<std::string> GetFileList(const std::string& path, const std::string& extension);
|
static std::vector<std::string> GetFileList(const std::string& path, const std::string& extension);
|
||||||
static std::vector<std::string> GetSysFileList(const std::string& path, const std::string& extension, bool folders = false);
|
static std::vector<std::string> GetSysFileList(const std::string& path, const std::string& extension, bool folders = false);
|
||||||
static bool DeleteFile(const std::string& folder, const std::string& file);
|
static bool DeleteFile(const std::string& folder, const std::string& file);
|
||||||
|
@ -153,11 +153,11 @@ namespace Components
|
|||||||
je useCustomRatio;
|
je useCustomRatio;
|
||||||
|
|
||||||
// execute switch statement code
|
// execute switch statement code
|
||||||
push 0x005063FC;
|
push 0x5063FC;
|
||||||
retn;
|
retn;
|
||||||
|
|
||||||
goToDefaultCase:
|
goToDefaultCase:
|
||||||
push 0x005064FC;
|
push 0x5064FC;
|
||||||
retn;
|
retn;
|
||||||
|
|
||||||
useCustomRatio:
|
useCustomRatio:
|
||||||
@ -170,7 +170,7 @@ namespace Components
|
|||||||
mov eax, 1;
|
mov eax, 1;
|
||||||
|
|
||||||
// continue execution
|
// continue execution
|
||||||
push 0x00506495;
|
push 0x506495;
|
||||||
retn;
|
retn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -240,6 +240,20 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QuickPatch::Sys_SpawnQuitProcess_Hk()
|
||||||
|
{
|
||||||
|
if (*Game::sys_exitCmdLine[0] == '\0')
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto workingDir = std::filesystem::current_path().string();
|
||||||
|
auto dir = FileSystem::GetAppdataPath() / "data" / "iw4x" / *Game::sys_exitCmdLine;
|
||||||
|
|
||||||
|
SetEnvironmentVariableA("XLABS_MW2_INSTALL", workingDir.data());
|
||||||
|
Utils::Library::LaunchProcess(dir.string(), "-singleplayer", workingDir);
|
||||||
|
}
|
||||||
|
|
||||||
Game::dvar_t* QuickPatch::Dvar_RegisterConMinicon(const char* dvarName, [[maybe_unused]] bool value, unsigned __int16 flags, const char* description)
|
Game::dvar_t* QuickPatch::Dvar_RegisterConMinicon(const char* dvarName, [[maybe_unused]] bool value, unsigned __int16 flags, const char* description)
|
||||||
{
|
{
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
@ -274,7 +288,10 @@ namespace Components
|
|||||||
|
|
||||||
Utils::Hook(0x4FA448, QuickPatch::Dvar_RegisterConMinicon, HOOK_CALL).install()->quick();
|
Utils::Hook(0x4FA448, QuickPatch::Dvar_RegisterConMinicon, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
Utils::Hook::Set<void(*)(Game::XAssetHeader, void*)>(0x51FCDD, R_AddImageToList_Hk);
|
Utils::Hook::Set<void(*)(Game::XAssetHeader, void*)>(0x51FCDD, QuickPatch::R_AddImageToList_Hk);
|
||||||
|
|
||||||
|
Utils::Hook::Set<const char*>(0x41DB8C, "iw4x-sp.exe");
|
||||||
|
Utils::Hook(0x4D6989, QuickPatch::Sys_SpawnQuitProcess_Hk, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
// protocol version (workaround for hacks)
|
// protocol version (workaround for hacks)
|
||||||
Utils::Hook::Set<int>(0x4FB501, PROTOCOL);
|
Utils::Hook::Set<int>(0x4FB501, PROTOCOL);
|
||||||
|
@ -30,6 +30,8 @@ namespace Components
|
|||||||
|
|
||||||
static void R_AddImageToList_Hk(Game::XAssetHeader header, void* data);
|
static void R_AddImageToList_Hk(Game::XAssetHeader header, void* data);
|
||||||
|
|
||||||
|
static void Sys_SpawnQuitProcess_Hk();
|
||||||
|
|
||||||
static Game::dvar_t* Dvar_RegisterConMinicon(const char* dvarName, bool value, unsigned __int16 flags, const char* description);
|
static Game::dvar_t* Dvar_RegisterConMinicon(const char* dvarName, bool value, unsigned __int16 flags, const char* description);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -631,6 +631,8 @@ namespace Game
|
|||||||
unsigned int* playerCardUIStringIndex = reinterpret_cast<unsigned int*>(0x62CD7A8);
|
unsigned int* playerCardUIStringIndex = reinterpret_cast<unsigned int*>(0x62CD7A8);
|
||||||
char (*playerCardUIStringBuf)[PLAYER_CARD_UI_STRING_COUNT][38] = reinterpret_cast<char(*)[PLAYER_CARD_UI_STRING_COUNT][38]>(0x62CB4F8);
|
char (*playerCardUIStringBuf)[PLAYER_CARD_UI_STRING_COUNT][38] = reinterpret_cast<char(*)[PLAYER_CARD_UI_STRING_COUNT][38]>(0x62CB4F8);
|
||||||
|
|
||||||
|
char (*sys_exitCmdLine)[1024] = reinterpret_cast<char(*)[1024]>(0x649FB68);
|
||||||
|
|
||||||
GamerSettingState* gamerSettings = reinterpret_cast<GamerSettingState*>(0x107D3E8);
|
GamerSettingState* gamerSettings = reinterpret_cast<GamerSettingState*>(0x107D3E8);
|
||||||
|
|
||||||
void Sys_LockRead(FastCriticalSection* critSect)
|
void Sys_LockRead(FastCriticalSection* critSect)
|
||||||
|
@ -1303,6 +1303,8 @@ namespace Game
|
|||||||
extern unsigned int* playerCardUIStringIndex;
|
extern unsigned int* playerCardUIStringIndex;
|
||||||
extern char (*playerCardUIStringBuf)[PLAYER_CARD_UI_STRING_COUNT][38];
|
extern char (*playerCardUIStringBuf)[PLAYER_CARD_UI_STRING_COUNT][38];
|
||||||
|
|
||||||
|
extern char (*sys_exitCmdLine)[1024];
|
||||||
|
|
||||||
extern GamerSettingState* gamerSettings;
|
extern GamerSettingState* gamerSettings;
|
||||||
|
|
||||||
void Sys_LockRead(FastCriticalSection* critSect);
|
void Sys_LockRead(FastCriticalSection* critSect);
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <WinSock2.h>
|
#include <WinSock2.h>
|
||||||
|
#include <ShlObj.h>
|
||||||
#include <timeapi.h>
|
#include <timeapi.h>
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
#include <WinInet.h>
|
#include <WinInet.h>
|
||||||
|
@ -19,20 +19,18 @@ namespace Utils
|
|||||||
return Library(handle);
|
return Library(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
Library::Library(const std::string& name, bool _freeOnDestroy) : module_(nullptr), freeOnDestroy(_freeOnDestroy)
|
Library::Library(const std::string& name, bool freeOnDestroy) : module_(nullptr), freeOnDestroy_(freeOnDestroy)
|
||||||
{
|
{
|
||||||
this->module_ = LoadLibraryExA(name.data(), nullptr, 0);
|
this->module_ = LoadLibraryExA(name.data(), nullptr, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Library::Library(const HMODULE handle)
|
Library::Library(const HMODULE handle) : module_(handle), freeOnDestroy_(true)
|
||||||
{
|
{
|
||||||
this->module_ = handle;
|
|
||||||
this->freeOnDestroy = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Library::~Library()
|
Library::~Library()
|
||||||
{
|
{
|
||||||
if (this->freeOnDestroy)
|
if (this->freeOnDestroy_)
|
||||||
{
|
{
|
||||||
this->free();
|
this->free();
|
||||||
}
|
}
|
||||||
@ -63,6 +61,39 @@ namespace Utils
|
|||||||
return this->module_;
|
return this->module_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Library::getName() const
|
||||||
|
{
|
||||||
|
if (!this->isValid())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto path = this->getPath();
|
||||||
|
const auto pos = path.find_last_of("/\\");
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
return path;
|
||||||
|
|
||||||
|
return path.substr(pos + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Library::getPath() const
|
||||||
|
{
|
||||||
|
if (!this->isValid())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
char name[MAX_PATH] = {0};
|
||||||
|
GetModuleFileNameA(this->module_, name, sizeof(name));
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Library::getFolder() const
|
||||||
|
{
|
||||||
|
if (!this->isValid())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const auto path = std::filesystem::path(this->getPath());
|
||||||
|
return path.parent_path().generic_string();
|
||||||
|
}
|
||||||
|
|
||||||
void Library::free()
|
void Library::free()
|
||||||
{
|
{
|
||||||
if (this->isValid())
|
if (this->isValid())
|
||||||
@ -72,4 +103,23 @@ namespace Utils
|
|||||||
|
|
||||||
this->module_ = nullptr;
|
this->module_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Library::LaunchProcess(const std::string& process, const std::string& commandLine, const std::string& currentDir)
|
||||||
|
{
|
||||||
|
STARTUPINFOA startup_info;
|
||||||
|
PROCESS_INFORMATION process_info;
|
||||||
|
|
||||||
|
ZeroMemory(&startup_info, sizeof(startup_info));
|
||||||
|
ZeroMemory(&process_info, sizeof(process_info));
|
||||||
|
startup_info.cb = sizeof(startup_info);
|
||||||
|
|
||||||
|
CreateProcessA(process.data(), const_cast<char*>(commandLine.data()), nullptr,
|
||||||
|
nullptr, false, NULL, nullptr, currentDir.data(),
|
||||||
|
&startup_info, &process_info);
|
||||||
|
|
||||||
|
if (process_info.hThread && process_info.hThread != INVALID_HANDLE_VALUE)
|
||||||
|
CloseHandle(process_info.hThread);
|
||||||
|
if (process_info.hProcess && process_info.hProcess != INVALID_HANDLE_VALUE)
|
||||||
|
CloseHandle(process_info.hProcess);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,9 @@ namespace Utils
|
|||||||
static Library Load(const std::filesystem::path& path);
|
static Library Load(const std::filesystem::path& path);
|
||||||
static Library GetByAddress(void* address);
|
static Library GetByAddress(void* address);
|
||||||
|
|
||||||
Library() : module_(nullptr), freeOnDestroy(false) {};
|
Library() : module_(nullptr), freeOnDestroy_(false) {}
|
||||||
Library(const std::string& name, bool freeOnDestroy);
|
Library(const std::string& name, bool freeOnDestroy);
|
||||||
explicit Library(const std::string& name) : module_(GetModuleHandleA(name.data())), freeOnDestroy(true) {};
|
explicit Library(const std::string& name) : module_(GetModuleHandleA(name.data())), freeOnDestroy_(true) {}
|
||||||
explicit Library(HMODULE handle);
|
explicit Library(HMODULE handle);
|
||||||
~Library();
|
~Library();
|
||||||
|
|
||||||
@ -23,6 +23,11 @@ namespace Utils
|
|||||||
|
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
HMODULE getModule() const;
|
HMODULE getModule() const;
|
||||||
|
std::string getName() const;
|
||||||
|
std::string getPath() const;
|
||||||
|
std::string getFolder() const;
|
||||||
|
std::uint8_t* getPtr() const;
|
||||||
|
void free();
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T getProc(const std::string& process) const
|
T getProc(const std::string& process) const
|
||||||
@ -62,10 +67,10 @@ namespace Utils
|
|||||||
return T();
|
return T();
|
||||||
}
|
}
|
||||||
|
|
||||||
void free();
|
static void LaunchProcess(const std::string& process, const std::string& commandLine, const std::string& currentDir);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HMODULE module_;
|
HMODULE module_;
|
||||||
bool freeOnDestroy;
|
bool freeOnDestroy_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -92,14 +92,14 @@ namespace Utils
|
|||||||
return elems;
|
return elems;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Replace(std::string &string, const std::string& find, const std::string& replace)
|
void Replace(std::string& str, const std::string& from, const std::string& to)
|
||||||
{
|
{
|
||||||
size_t nPos = 0;
|
std::size_t nPos = 0;
|
||||||
|
|
||||||
while ((nPos = string.find(find, nPos)) != std::string::npos)
|
while ((nPos = str.find(from, nPos)) != std::string::npos)
|
||||||
{
|
{
|
||||||
string = string.replace(nPos, find.length(), replace);
|
str = str.replace(nPos, from.length(), to);
|
||||||
nPos += replace.length();
|
nPos += to.length();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ namespace Utils
|
|||||||
std::string ToUpper(std::string text);
|
std::string ToUpper(std::string text);
|
||||||
bool Compare(const std::string& lhs, const std::string& rhs);
|
bool Compare(const std::string& lhs, const std::string& rhs);
|
||||||
std::vector<std::string> Split(const std::string& str, char delim);
|
std::vector<std::string> Split(const std::string& str, char delim);
|
||||||
void Replace(std::string& string, const std::string& find, const std::string& replace);
|
void Replace(std::string& str, const std::string& from, const std::string& to);
|
||||||
bool StartsWith(const std::string& haystack, const std::string& needle);
|
bool StartsWith(const std::string& haystack, const std::string& needle);
|
||||||
bool EndsWith(const std::string& haystack, const std::string& needle);
|
bool EndsWith(const std::string& haystack, const std::string& needle);
|
||||||
bool IsNumber(const std::string& str);
|
bool IsNumber(const std::string& str);
|
||||||
|
Loading…
Reference in New Issue
Block a user