r5sdk/r5dev/sdklauncher/sdklauncher.cpp
Kawe Mazidjatari f2a5c8ac68 See description
* Renamed 'r5apexsdkd64.dll' to 'gamesdk.dll'.
* Added required dedicated parameters to code instead.
* Bug fixes around CCommandLine class (fixed misaligned VTable indexes).
* SDK now supports being directly launched by the game executable.

The SDK launcher will pass '-launcher' to the game, which indicated its being launched by the launcher. If the game does not receive '-launcher', it assumes its being launched directly from the game executable, which will instead load 'startup_(dedi_)default.cfg'.

The sdk dll's are now added to the game's IAT by their dummy exports allowing for them to be loaded when the exe is loaded (the dll's do everything on init).
2022-04-16 00:30:46 +02:00

298 lines
13 KiB
C++

#include "core/stdafx.h"
#include "sdklauncher.h"
//-----------------------------------------------------------------------------
// Purpose switch case:
// * Launch the game in user specified mode and state.
// * Load specified command line arguments from a file on the disk.
// * Format the file paths for the game exe and specified hook dll.
//-----------------------------------------------------------------------------
bool LaunchR5Apex(eLaunchMode lMode, eLaunchState lState)
{
///////////////////////////////////////////////////////////////////////////
// Initialize strings.
std::string svWorkerDll = std::string();
std::string svGameDir = std::string();
std::string svCmdLineArgs = std::string();
std::string svStartCmdLine = std::string();
std::string svCurrentDir = std::filesystem::current_path().u8string();
///////////////////////////////////////////////////////////////////////////
// Determine launch mode.
switch (lMode)
{
case eLaunchMode::LM_DEBUG_GAME:
{
std::filesystem::path cfgPath = std::filesystem::current_path() /= "platform\\cfg\\startup_debug.cfg"; // Get cfg path for debug startup.
std::ifstream cfgFile(cfgPath); // Read the cfg file.
if (cfgFile.good() && cfgFile) // Does the cfg file exist?
{
std::stringstream ss;
ss << cfgFile.rdbuf(); // Read ifstream buffer into stringstream.
svCmdLineArgs = ss.str() + "-launcher"; // Get all the contents of the cfg file.
}
else
{
spdlog::error("File 'platform\\cfg\\startup_debug.cfg' does not exist!\n");
cfgFile.close();
return false;
}
cfgFile.close(); // Close cfg file.
svWorkerDll = svCurrentDir + "\\gamesdk.dll"; // Get path to worker dll.
svGameDir = svCurrentDir + "\\r5apex.exe"; // Get path to game executeable.
svStartCmdLine = svCurrentDir + "\\r5apex.exe " + svCmdLineArgs; // Setup startup command line string.
spdlog::info("*** LAUNCHING GAME [DEBUG] ***\n");
break;
}
case eLaunchMode::LM_RELEASE_GAME:
{
std::filesystem::path cfgPath = std::filesystem::current_path() /= "platform\\cfg\\startup_retail.cfg"; // Get cfg path for release startup.
std::ifstream cfgFile(cfgPath); // Read the cfg file.
if (cfgFile.good() && cfgFile) // Does the cfg file exist?
{
std::stringstream ss;
ss << cfgFile.rdbuf(); // Read ifstream buffer into stringstream.
svCmdLineArgs = ss.str() + "-launcher"; // Get all the contents of the cfg file.
}
else
{
spdlog::error("File 'platform\\cfg\\startup_retail.cfg' does not exist!\n");
cfgFile.close();
return false;
}
cfgFile.close(); // Close cfg file.
svWorkerDll = svCurrentDir + "\\gamesdk.dll"; // Get path to worker dll.
svGameDir = svCurrentDir + "\\r5apex.exe"; // Get path to game executeable.
svStartCmdLine = svCurrentDir + "\\r5apex.exe " + svCmdLineArgs; // Setup startup command line string.
spdlog::info("*** LAUNCHING GAME [RELEASE] ***\n");
break;
}
case eLaunchMode::LM_DEBUG_DEDI:
{
std::filesystem::path cfgPath = std::filesystem::current_path() /= "platform\\cfg\\startup_dedi_debug.cfg"; // Get cfg path for dedicated startup.
std::ifstream cfgFile(cfgPath); // Read the cfg file.
if (cfgFile.good() && cfgFile) // Does the cfg file exist?
{
std::stringstream ss;
ss << cfgFile.rdbuf(); // Read ifstream buffer into stringstream.
svCmdLineArgs = ss.str() + "-launcher"; // Get all the contents of the cfg file.
}
else
{
spdlog::error("File 'platform\\cfg\\startup_dedi_debug.cfg' does not exist!\n");
cfgFile.close();
return false;
}
cfgFile.close(); // Close cfg file.
svWorkerDll = svCurrentDir + "\\dedicated.dll"; // Get path to worker dll.
svGameDir = svCurrentDir + "\\r5apex_ds.exe"; // Get path to game executeable.
svStartCmdLine = svCurrentDir + "\\r5apex_ds.exe " + svCmdLineArgs; // Setup startup command line string.
spdlog::info("*** LAUNCHING DEDICATED [DEBUG] ***\n");
break;
}
case eLaunchMode::LM_RELEASE_DEDI:
{
std::filesystem::path cfgPath = std::filesystem::current_path() /= "platform\\cfg\\startup_dedi_retail.cfg"; // Get cfg path for dedicated startup.
std::ifstream cfgFile(cfgPath); // Read the cfg file.
if (cfgFile.good() && cfgFile) // Does the cfg file exist?
{
std::stringstream ss;
ss << cfgFile.rdbuf(); // Read ifstream buffer into stringstream.
svCmdLineArgs = ss.str(); // Get all the contents of the cfg file.
}
else
{
spdlog::error("File 'platform\\cfg\\startup_dedi_retail.cfg' does not exist!\n");
cfgFile.close();
return false;
}
cfgFile.close(); // Close cfg file.
svWorkerDll = svCurrentDir + "\\dedicated.dll"; // Get path to worker dll.
svGameDir = svCurrentDir + "\\r5apex_ds.exe"; // Get path to game executeable.
svStartCmdLine = svCurrentDir + "\\r5apex_ds.exe " + svCmdLineArgs; // Setup startup command line string.
spdlog::info("*** LAUNCHING DEDICATED [RELEASE] ***\n");
break;
}
default:
{
spdlog::error("*** NO LAUNCH MODE SPECIFIED ***\n");
return false;
}
}
///////////////////////////////////////////////////////////////////////////
// Print the file paths and arguments.
std::cout << "----------------------------------------------------------------------------------------------------------------------" << std::endl;
spdlog::debug("- CWD: {}\n", svCurrentDir);
spdlog::debug("- EXE: {}\n", svGameDir);
spdlog::debug("- DLL: {}\n", svWorkerDll);
spdlog::debug("- CLI: {}\n", svCmdLineArgs);
std::cout << "----------------------------------------------------------------------------------------------------------------------" << std::endl;
///////////////////////////////////////////////////////////////////////////
// Build our list of dlls to inject.
LPCSTR DllsToInject[1] =
{
svWorkerDll.c_str()
};
STARTUPINFOA StartupInfo = { 0 };
PROCESS_INFORMATION ProcInfo = { 0 };
// Initialize startup info struct.
StartupInfo.cb = sizeof(STARTUPINFOA);
///////////////////////////////////////////////////////////////////////////
// Create the game process in a suspended state with our dll.
BOOL result = DetourCreateProcessWithDllsA
(
svGameDir.c_str(), // lpApplicationName
(LPSTR)svStartCmdLine.c_str(), // lpCommandLine
NULL, // lpProcessAttributes
NULL, // lpThreadAttributes
FALSE, // bInheritHandles
CREATE_SUSPENDED, // dwCreationFlags
NULL, // lpEnvironment
svCurrentDir.c_str(), // lpCurrentDirectory
&StartupInfo, // lpStartupInfo
&ProcInfo, // lpProcessInformation
sizeof(DllsToInject) / sizeof(LPCSTR), // nDlls
DllsToInject, // rlpDlls
NULL // pfCreateProcessA
);
///////////////////////////////////////////////////////////////////////////
// Failed to create the process.
if (!result)
{
PrintLastError();
return false;
}
///////////////////////////////////////////////////////////////////////////
// Resume the process.
ResumeThread(ProcInfo.hThread);
///////////////////////////////////////////////////////////////////////////
// Close the process and thread handles.
CloseHandle(ProcInfo.hProcess);
CloseHandle(ProcInfo.hThread);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// Entrypoint.
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[], char* envp[])
{
spdlog::set_pattern("[%^%l%$] %v");
spdlog::set_level(spdlog::level::trace);
for (int i = 1; i < argc; ++i)
{
std::string arg = argv[i];
if ((arg == "-debug") || (arg == "-dbg"))
{
LaunchR5Apex(eLaunchMode::LM_DEBUG_GAME, eLaunchState::LS_CHEATS);
Sleep(2000);
return EXIT_SUCCESS;
}
if ((arg == "-release") || (arg == "-rel"))
{
LaunchR5Apex(eLaunchMode::LM_RELEASE_GAME, eLaunchState::LS_CHEATS);
Sleep(2000);
return EXIT_SUCCESS;
}
if ((arg == "-dedicated_dev") || (arg == "-dedid"))
{
LaunchR5Apex(eLaunchMode::LM_DEBUG_DEDI, eLaunchState::LS_CHEATS);
Sleep(2000);
return EXIT_SUCCESS;
}
if ((arg == "-dedicated") || (arg == "-dedi"))
{
LaunchR5Apex(eLaunchMode::LM_RELEASE_DEDI, eLaunchState::LS_CHEATS);
Sleep(2000);
return EXIT_SUCCESS;
}
}
std::cout << "----------------------------------------------------------------------------------------------------------------------" << std::endl;
spdlog::warn("If a DEBUG option has been choosen as launch parameter, do not broadcast servers to the Server Browser!\n");
spdlog::warn("All FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY ConVar's/ConCommand's will be enabled.\n");
spdlog::warn("Connected clients will be able to set and execute anything flagged FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY.\n");
std::cout << "----------------------------------------------------------------------------------------------------------------------" << std::endl;
spdlog::warn("Use DEBUG GAME [1] for research and development purposes.\n");
spdlog::warn("Use RELEASE GAME [2] for playing the game and creating servers.\n");
spdlog::warn("Use DEBUG DEDICATED [3] for research and development purposes.\n");
spdlog::warn("Use RELEASE DEDICATED [4] for running and hosting dedicated servers.\n");
std::cout << "----------------------------------------------------------------------------------------------------------------------" << std::endl;
spdlog::info("Enter '1' for 'DEBUG GAME'.\n");
spdlog::info("Enter '2' for 'RELEASE GAME'.\n");
spdlog::info("Enter '3' for 'DEBUG DEDICATED'.\n");
spdlog::info("Enter '4' for 'RELEASE DEDICATED'.\n");
std::cout << "----------------------------------------------------------------------------------------------------------------------" << std::endl;
std::cout << "User input: ";
std::string input = std::string();
if (std::cin >> input)
{
try
{
eLaunchMode mode = (eLaunchMode)std::stoi(input);
switch (mode)
{
case eLaunchMode::LM_DEBUG_GAME:
{
LaunchR5Apex(eLaunchMode::LM_DEBUG_GAME, eLaunchState::LS_CHEATS);
Sleep(2000);
return EXIT_SUCCESS;
}
case eLaunchMode::LM_RELEASE_GAME:
{
LaunchR5Apex(eLaunchMode::LM_RELEASE_GAME, eLaunchState::LS_CHEATS);
Sleep(2000);
return EXIT_SUCCESS;
}
case eLaunchMode::LM_DEBUG_DEDI:
{
LaunchR5Apex(eLaunchMode::LM_DEBUG_DEDI, eLaunchState::LS_CHEATS);
Sleep(2000);
return EXIT_SUCCESS;
}
case eLaunchMode::LM_RELEASE_DEDI:
{
LaunchR5Apex(eLaunchMode::LM_RELEASE_DEDI, eLaunchState::LS_CHEATS);
Sleep(2000);
return EXIT_SUCCESS;
}
default:
{
spdlog::error("R5Reloaded requires '1' for DEBUG GAME mode, '2' for RELEASE GAME mode, '3' for DEBUG DEDICATED mode, '4' for RELEASE DEDICATED mode.\n");
Sleep(5000);
return EXIT_FAILURE;
}
}
}
catch (std::exception& e)
{
spdlog::error("R5Reloaded only takes numerical input to launch. Error: {}.\n", e.what());
Sleep(5000);
return EXIT_FAILURE;
}
}
spdlog::error("R5Reloaded requires numerical input to launch.\n");
Sleep(5000);
return EXIT_FAILURE;
}