enderalse/source/fs.dll/skse64/skse64_loader/main.cpp

390 lines
8.8 KiB
C++

#include <ShlObj.h>
#include "skse64_common/skse_version.h"
#include "skse64_common/Utilities.h"
#include "skse64_loader_common/LoaderError.h"
#include "skse64_loader_common/IdentifyEXE.h"
#include "skse64_loader_common/Steam.h"
#include "skse64_loader_common/Inject.h"
#include <string>
#include "common/IFileStream.h"
#include <tlhelp32.h>
#include "Options.h"
IDebugLog gLog;
static void PrintModuleInfo(UInt32 procID);
static void PrintProcessInfo();
int main(int argc, char ** argv)
{
gLog.OpenRelative(CSIDL_MYDOCUMENTS, "\\My Games\\Skyrim Special Edition\\SKSE\\skse64_loader.log");
gLog.SetPrintLevel(IDebugLog::kLevel_FatalError);
gLog.SetLogLevel(IDebugLog::kLevel_DebugMessage);
FILETIME now;
GetSystemTimeAsFileTime(&now);
_MESSAGE("skse64 loader %08X %08X%08X %s",
PACKED_SKSE_VERSION, now.dwHighDateTime, now.dwLowDateTime, GetOSInfoStr().c_str());
if(!g_options.Read(argc, argv))
{
PrintLoaderError("Couldn't read arguments.");
g_options.PrintUsage();
return 1;
}
if(g_options.m_optionsOnly)
{
g_options.PrintUsage();
return 0;
}
if(g_options.m_verbose)
gLog.SetPrintLevel(IDebugLog::kLevel_VerboseMessage);
if(g_options.m_launchCS)
{
PrintLoaderError("The editor should be launched directly.");
return 1;
}
// get process/dll names
bool dllHasFullPath = false;
const char * baseDllName = g_options.m_launchCS ? "skse64_editor" : "skse64";
bool usedCustomRuntimeName = false;
std::string procName;
if(g_options.m_launchCS)
{
procName = "CreationKit.exe";
}
else
{
procName = GetConfigOption("Loader", "RuntimeName");
if(!procName.empty())
{
_MESSAGE("using runtime name from config: %s", procName.c_str());
usedCustomRuntimeName = true;
}
else
{
procName = "SkyrimSE.exe";
// simple check to see if someone kludge-patched the EXE
// don't kludge the EXE, use the .ini file RIGHT ABOVE HERE
UInt32 procNameCheck =
(procName[0] << 8) |
(procName[1] << 24) |
(procName[2] << 16) |
(procName[3] << 0);
if(procNameCheck != 'kySr')
{
_ERROR("### someone kludged the default process name to (%s), don't ask me for support with your install ###", procName.c_str());
}
// check to see if someone screwed up their install
std::string appName = GetRuntimeName();
if(!_stricmp(appName.c_str(), procName.c_str()))
{
PrintLoaderError("You have renamed skse64_loader and have not specified the name of the runtime.");
return 1;
}
}
}
const std::string & runtimeDir = GetRuntimeDirectory();
std::string procPath = runtimeDir + "\\" + procName;
if(g_options.m_altEXE.size())
{
procPath = g_options.m_altEXE;
_MESSAGE("launching alternate exe (%s)", procPath.c_str());
}
_MESSAGE("procPath = %s", procPath.c_str());
// check if the exe exists
{
IFileStream fileCheck;
if(!fileCheck.Open(procPath.c_str()))
{
if(usedCustomRuntimeName)
{
// hurr durr
PrintLoaderError("Couldn't find %s. You have customized the runtime name via SKSE64's .ini file, and that file does not exist. This can usually be fixed by removing the RuntimeName line from the .ini file.)", procName.c_str());
}
else
{
PrintLoaderError("Couldn't find %s.", procName.c_str());
}
return 1;
}
}
_MESSAGE("launching: %s (%s)", procName.c_str(), procPath.c_str());
if(g_options.m_altDLL.size())
{
baseDllName = g_options.m_altDLL.c_str();
_MESSAGE("launching alternate dll (%s)", baseDllName);
dllHasFullPath = true;
}
std::string dllSuffix;
ProcHookInfo procHookInfo;
// check exe version
if(!IdentifyEXE(procPath.c_str(), g_options.m_launchCS, &dllSuffix, &procHookInfo))
{
_ERROR("unknown exe");
if(usedCustomRuntimeName)
{
// hurr durr
PrintLoaderError("You have customized the runtime name via SKSE64's .ini file. Version errors can usually be fixed by removing the RuntimeName line from the .ini file.");
}
return 1;
}
if(g_options.m_crcOnly)
return 0;
// build dll path
std::string dllPath;
if(dllHasFullPath)
{
dllPath = baseDllName;
}
else
{
dllPath = runtimeDir + "\\" + baseDllName + "_" + dllSuffix + ".dll";
}
_MESSAGE("dll = %s", dllPath.c_str());
// check to make sure the dll exists
{
IFileStream tempFile;
if(!tempFile.Open(dllPath.c_str()))
{
PrintLoaderError("Couldn't find SKSE64 DLL (%s). Please make sure you have installed SKSE64 correctly and are running it from your Skyrim SE folder.", dllPath.c_str());
return 1;
}
}
// steam setup
if(procHookInfo.procType == kProcType_Steam)
{
if(g_options.m_launchSteam)
{
// if steam isn't running, launch it
if(!SteamCheckPassive())
{
_MESSAGE("steam not running, launching it");
if(!SteamLaunch())
{
_WARNING("failed to launch steam");
}
}
}
// same for standard and nogore
const char * kAppID = (g_options.m_launchCS == false ? "489830" : "???");
// set this no matter what to work around launch issues
SetEnvironmentVariable("SteamGameId", kAppID);
if(g_options.m_skipLauncher)
{
SetEnvironmentVariable("SteamAppID", kAppID);
}
}
// launch the app (suspended)
STARTUPINFO startupInfo = { 0 };
PROCESS_INFORMATION procInfo = { 0 };
startupInfo.cb = sizeof(startupInfo);
if(!CreateProcess(
procPath.c_str(),
NULL, // no args
NULL, // default process security
NULL, // default thread security
FALSE, // don't inherit handles
CREATE_SUSPENDED,
NULL, // no new environment
NULL, // no new cwd
&startupInfo, &procInfo))
{
if(GetLastError() == 740)
{
PrintLoaderError("Launching %s failed (%d). Please try running skse64_loader as an administrator.", procPath.c_str(), GetLastError());
}
else
{
PrintLoaderError("Launching %s failed (%d).", procPath.c_str(), GetLastError());
}
return 1;
}
_MESSAGE("main thread id = %d", procInfo.dwThreadId);
// set affinity if requested
if(g_options.m_affinity)
{
_MESSAGE("setting affinity mask to %016I64X", g_options.m_affinity);
if(!SetProcessAffinityMask(procInfo.hProcess, g_options.m_affinity))
{
_WARNING("couldn't set affinity mask (%08X)", GetLastError());
}
}
bool injectionSucceeded = false;
UInt32 procType = procHookInfo.procType;
if(g_options.m_forceSteamLoader)
{
_MESSAGE("forcing steam loader");
procType = kProcType_Steam;
}
// inject the dll
switch(procType)
{
case kProcType_Steam:
{
std::string steamHookDllPath = runtimeDir + "\\skse64_steam_loader.dll";
injectionSucceeded = InjectDLLThread(&procInfo, steamHookDllPath.c_str(), true, g_options.m_noTimeout);
}
break;
case kProcType_Normal:
#if 0
if(InjectDLL(&procInfo, dllPath.c_str(), &procHookInfo))
{
injectionSucceeded = true;
}
#else
injectionSucceeded = InjectDLLThread(&procInfo, dllPath.c_str(), true, g_options.m_noTimeout);
#endif
break;
default:
HALT("impossible");
}
// start the process if successful
if(!injectionSucceeded)
{
PrintLoaderError("Couldn't inject DLL.");
_ERROR("terminating process");
TerminateProcess(procInfo.hProcess, 0);
}
else
{
_MESSAGE("launching");
if(!ResumeThread(procInfo.hThread))
{
_WARNING("WARNING: something has started the runtime outside of skse64_loader's control.");
_WARNING("SKSE64 will probably not function correctly.");
_WARNING("Try running skse64_loader as an administrator, or check for conflicts with a virus scanner.");
}
if(g_options.m_moduleInfo)
{
Sleep(1000 * 3); // wait 3 seconds
PrintModuleInfo(procInfo.dwProcessId);
PrintProcessInfo();
}
if(g_options.m_waitForClose)
WaitForSingleObject(procInfo.hProcess, INFINITE);
}
// clean up
CloseHandle(procInfo.hProcess);
CloseHandle(procInfo.hThread);
return 0;
}
static void PrintModuleInfo(UInt32 procID)
{
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, procID);
if(snap != INVALID_HANDLE_VALUE)
{
MODULEENTRY32 module;
module.dwSize = sizeof(module);
if(Module32First(snap, &module))
{
do
{
_MESSAGE("%08Xx%08X %08X %s %s", module.modBaseAddr, module.modBaseSize, module.hModule, module.szModule, module.szExePath);
}
while(Module32Next(snap, &module));
}
else
{
_ERROR("PrintModuleInfo: Module32First failed (%d)", GetLastError());
}
CloseHandle(snap);
}
else
{
_ERROR("PrintModuleInfo: CreateToolhelp32Snapshot failed (%d)", GetLastError());
}
}
static void PrintProcessInfo()
{
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(snap != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32 proc;
proc.dwSize = sizeof(PROCESSENTRY32);
if(Process32First(snap, &proc))
{
do
{
_MESSAGE("%s", proc.szExeFile);
proc.dwSize = sizeof(PROCESSENTRY32);
}
while (Process32Next(snap, &proc));
}
else
{
_ERROR("PrintProcessInfo: Process32First failed (%d)", GetLastError());
}
CloseHandle(snap);
}
else
{
_ERROR("PrintProcessInfo: CreateToolhelp32Snapshot failed (%d)", GetLastError());
}
}