[QuickPatch] Make startsingleplayer command work with iw4x-sp

This commit is contained in:
Diavolo 2022-08-02 13:24:22 +02:00
parent 0fde17bbb2
commit fe73bb9827
No known key found for this signature in database
GPG Key ID: FA77F074E98D98A5
11 changed files with 130 additions and 34 deletions

View File

@ -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> fileList;

View File

@ -89,6 +89,7 @@ namespace Components
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> GetSysFileList(const std::string& path, const std::string& extension, bool folders = false);
static bool DeleteFile(const std::string& folder, const std::string& file);

View File

@ -153,11 +153,11 @@ namespace Components
je useCustomRatio;
// execute switch statement code
push 0x005063FC;
push 0x5063FC;
retn;
goToDefaultCase:
push 0x005064FC;
push 0x5064FC;
retn;
useCustomRatio:
@ -170,7 +170,7 @@ namespace Components
mov eax, 1;
// continue execution
push 0x00506495;
push 0x506495;
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)
{
#ifdef _DEBUG
@ -274,7 +288,10 @@ namespace Components
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)
Utils::Hook::Set<int>(0x4FB501, PROTOCOL);

View File

@ -30,6 +30,8 @@ namespace Components
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);
};
}

View File

@ -631,6 +631,8 @@ namespace Game
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 (*sys_exitCmdLine)[1024] = reinterpret_cast<char(*)[1024]>(0x649FB68);
GamerSettingState* gamerSettings = reinterpret_cast<GamerSettingState*>(0x107D3E8);
void Sys_LockRead(FastCriticalSection* critSect)

View File

@ -1303,6 +1303,8 @@ namespace Game
extern unsigned int* playerCardUIStringIndex;
extern char (*playerCardUIStringBuf)[PLAYER_CARD_UI_STRING_COUNT][38];
extern char (*sys_exitCmdLine)[1024];
extern GamerSettingState* gamerSettings;
void Sys_LockRead(FastCriticalSection* critSect);

View File

@ -16,6 +16,7 @@
#include <Windows.h>
#include <WinSock2.h>
#include <ShlObj.h>
#include <timeapi.h>
#include <shellapi.h>
#include <WinInet.h>

View File

@ -19,20 +19,18 @@ namespace Utils
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);
}
Library::Library(const HMODULE handle)
Library::Library(const HMODULE handle) : module_(handle), freeOnDestroy_(true)
{
this->module_ = handle;
this->freeOnDestroy = true;
}
Library::~Library()
{
if (this->freeOnDestroy)
if (this->freeOnDestroy_)
{
this->free();
}
@ -63,6 +61,39 @@ namespace Utils
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()
{
if (this->isValid())
@ -72,4 +103,23 @@ namespace Utils
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);
}
}

View File

@ -9,9 +9,9 @@ namespace Utils
static Library Load(const std::filesystem::path& path);
static Library GetByAddress(void* address);
Library() : module_(nullptr), freeOnDestroy(false) {};
Library() : module_(nullptr), freeOnDestroy_(false) {}
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);
~Library();
@ -23,6 +23,11 @@ namespace Utils
bool isValid() 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>
T getProc(const std::string& process) const
@ -62,10 +67,10 @@ namespace Utils
return T();
}
void free();
static void LaunchProcess(const std::string& process, const std::string& commandLine, const std::string& currentDir);
private:
HMODULE module_;
bool freeOnDestroy;
bool freeOnDestroy_;
};
}

View File

@ -92,14 +92,14 @@ namespace Utils
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);
nPos += replace.length();
str = str.replace(nPos, from.length(), to);
nPos += to.length();
}
}

View File

@ -78,7 +78,7 @@ namespace Utils
std::string ToUpper(std::string text);
bool Compare(const std::string& lhs, const std::string& rhs);
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 EndsWith(const std::string& haystack, const std::string& needle);
bool IsNumber(const std::string& str);