launchdf rewrite

this rewrites launchdf so that the dfhack launcher attempts to linger while df is running
develop
Kelly Kinkade 2023-04-18 08:21:54 -05:00 committed by Kelly Kinkade
parent 150708f21d
commit f336771284
2 changed files with 155 additions and 22 deletions

@ -314,10 +314,10 @@ if(WIN32)
endif() endif()
# download Steam SDK # download Steam SDK
set (STEAMAPI_DIR ${dfhack_SOURCE_DIR}/depends/steam/) set (STEAMAPI_DIR ${dfhack_SOURCE_DIR}/depends/steam)
download_file("https://partner.steamgames.com/downloads/steamworks_sdk_156.zip" file(DOWNLOAD "https://partner.steamgames.com/downloads/steamworks_sdk_156.zip"
${STEAMAPI_DIR}/steamworks_sdk_156.zip ${STEAMAPI_DIR}/steamworks_sdk_156.zip
"af5a579990dbe5ae4c1b0689260d001b") EXPECTED_HASH MD5=af5a579990dbe5ae4c1b0689260d001b)
file(ARCHIVE_EXTRACT file(ARCHIVE_EXTRACT
INPUT ${STEAMAPI_DIR}/steamworks_sdk_156.zip INPUT ${STEAMAPI_DIR}/steamworks_sdk_156.zip
DESTINATION ${STEAMAPI_DIR}) DESTINATION ${STEAMAPI_DIR})

@ -1,8 +1,12 @@
#include <process.h> #include <process.h>
#include <windows.h> #include <windows.h>
#include <TlHelp32.h>
#include "steam_api.h" #include "steam_api.h"
#include <string>
const uint32 DFHACK_STEAM_APPID = 2346660; const uint32 DFHACK_STEAM_APPID = 2346660;
const uint32 DF_STEAM_APPID = 975370;
static BOOL is_running_on_wine() { static BOOL is_running_on_wine() {
typedef const char* (CDECL wine_get_version)(void); typedef const char* (CDECL wine_get_version)(void);
@ -32,7 +36,7 @@ static LPCWSTR launch_via_steam_windows() {
si.cb = sizeof(si); si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi)); ZeroMemory(&pi, sizeof(pi));
WCHAR steamPath[1024]; WCHAR steamPath[1024] = L"";
DWORD datasize = 1024; DWORD datasize = 1024;
LONG retCode = RegGetValueW(HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam", LONG retCode = RegGetValueW(HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam",
@ -43,16 +47,24 @@ static LPCWSTR launch_via_steam_windows() {
WCHAR commandLine[1024] = L"steam.exe -applaunch 975370"; WCHAR commandLine[1024] = L"steam.exe -applaunch 975370";
if (CreateProcessW(steamPath, commandLine, BOOL res = CreateProcessW(steamPath, commandLine,
NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi) == 0) NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
return L"Could not launch Dwarf Fortress";
if (res)
{
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return NULL; return NULL;
} }
else
{
return L"Could not launch Dwarf Fortress";
}
}
// this method doesn't properly attribute Steam playtime metrics to DF, static LPCWSTR launch_direct() {
// but that's better than not having DF start at all.
static BOOL launch_direct() {
STARTUPINFOW si; STARTUPINFOW si;
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
@ -60,31 +72,152 @@ static BOOL launch_direct() {
si.cb = sizeof(si); si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi)); ZeroMemory(&pi, sizeof(pi));
return CreateProcessW(L"Dwarf Fortress.exe", BOOL res = CreateProcessW(L"Dwarf Fortress.exe",
NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
if (res)
{
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return NULL;
}
return L"Could not launch via non-steam fallback method";
}
DWORD findDwarfFortressProcess()
{
PROCESSENTRY32W entry;
entry.dwSize = sizeof(PROCESSENTRY32W);
const auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (!Process32FirstW(snapshot, &entry))
{
CloseHandle(snapshot);
return -1;
} }
int WINAPI wWinMain(HINSTANCE hi, HINSTANCE hpi, PWSTR cmd, int ns) { do {
std::wstring executableName(entry.szExeFile);
if (executableName == L"Dwarf Fortress.exe")
{
CloseHandle(snapshot);
return entry.th32ProcessID;
}
} while (Process32NextW(snapshot, &entry));
if (SteamAPI_RestartAppIfNecessary(DFHACK_STEAM_APPID)) // Replace with your App ID CloseHandle(snapshot);
return -1;
}
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nShowCmd) {
// initialize steam context
if (SteamAPI_RestartAppIfNecessary(DFHACK_STEAM_APPID))
{ {
return 1; exit(0);
} }
if (!SteamAPI_Init()) if (!SteamAPI_Init())
{ {
printf("Fatal Error - Steam must be running to play this game (SteamAPI_Init() failed).\n"); // could not initialize steam context, attempt fallback launch
return 1; LPCWSTR err = launch_direct();
if (err != NULL)
{
MessageBoxW(NULL, err, NULL, 0);
exit(1);
}
exit(0);
}
bool wine = is_running_on_wine();
if (wine)
{
// attempt launch via steam client
LPCWSTR err = launch_via_steam_posix();
if (err != NULL)
// steam client launch failed, attempt fallback launch
err = launch_direct();
if (err != NULL)
{
MessageBoxW(NULL, err, NULL, 0);
exit(1);
}
exit(0);
}
// steam detected and not running in wine
bool df_installed = SteamApps()->BIsAppInstalled(DF_STEAM_APPID);
if (!df_installed)
{
// Steam DF is not installed. Assume DF is installed in same directory as DFHack and do a fallback launch
LPCWSTR err = launch_direct();
if (err != NULL)
{
MessageBoxW(NULL, err, NULL, 0);
exit(1);
}
exit(0);
} }
return 0; // obtain DF app path
char buf[2048] = "";
LPCWSTR err = is_running_on_wine() ? launch_via_steam_posix() : launch_via_steam_windows(); int b = SteamApps()->GetAppInstallDir(DFHACK_STEAM_APPID, (char*)&buf, 2048);
std::string dfhack_install_folder = (b != -1) ? std::string(buf) : "";
if (err && !launch_direct()) { int b2 = SteamApps()->GetAppInstallDir(DF_STEAM_APPID, (char*)&buf, 2048);
std::string df_install_folder = (b != -1) ? std::string(buf) : "";
if (df_install_folder != dfhack_install_folder)
{
// DF and DFHack are not installed in the same library
MessageBoxW(NULL, L"DFHack and Dwarf Fortress must be installed in the same Steam library.\nAborting.", NULL, 0);
exit(1);
}
DWORD df_pid = findDwarfFortressProcess();
if (df_pid == -1)
{
LPCWSTR err = launch_via_steam_windows();
if (err != NULL)
{
MessageBoxW(NULL, err, NULL, 0); MessageBoxW(NULL, err, NULL, 0);
exit(1); exit(1);
} }
int counter = 0;
do {
if (counter++ > 60)
{
MessageBoxW(NULL, L"Dwarf Fortress took too long to launch, aborting", NULL, 0);
exit(1);
}
Sleep(1000);
df_pid = findDwarfFortressProcess();
} while (df_pid == -1);
}
HANDLE hDF = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, df_pid);
// in the future open an IPC connection so that we can proxy SteamAPI calls for the DFSteam module
// this will eventuallyh need to become a loop with a WaitForMultipleObjects call
WaitForSingleObject(hDF, INFINITE);
CloseHandle(hDF);
exit(0); exit(0);
} }