From f336771284b152684cc4eb22cb6856330c649faf Mon Sep 17 00:00:00 2001 From: Kelly Kinkade Date: Tue, 18 Apr 2023 08:21:54 -0500 Subject: [PATCH] launchdf rewrite this rewrites launchdf so that the dfhack launcher attempts to linger while df is running --- CMakeLists.txt | 6 +- package/windows/launchdf.cpp | 171 +++++++++++++++++++++++++++++++---- 2 files changed, 155 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 899063d9d..cf5586dfd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -314,10 +314,10 @@ if(WIN32) endif() # download Steam SDK - set (STEAMAPI_DIR ${dfhack_SOURCE_DIR}/depends/steam/) - download_file("https://partner.steamgames.com/downloads/steamworks_sdk_156.zip" + set (STEAMAPI_DIR ${dfhack_SOURCE_DIR}/depends/steam) + file(DOWNLOAD "https://partner.steamgames.com/downloads/steamworks_sdk_156.zip" ${STEAMAPI_DIR}/steamworks_sdk_156.zip - "af5a579990dbe5ae4c1b0689260d001b") + EXPECTED_HASH MD5=af5a579990dbe5ae4c1b0689260d001b) file(ARCHIVE_EXTRACT INPUT ${STEAMAPI_DIR}/steamworks_sdk_156.zip DESTINATION ${STEAMAPI_DIR}) diff --git a/package/windows/launchdf.cpp b/package/windows/launchdf.cpp index 804163960..19128ead1 100644 --- a/package/windows/launchdf.cpp +++ b/package/windows/launchdf.cpp @@ -1,8 +1,12 @@ #include #include +#include #include "steam_api.h" +#include + const uint32 DFHACK_STEAM_APPID = 2346660; +const uint32 DF_STEAM_APPID = 975370; static BOOL is_running_on_wine() { typedef const char* (CDECL wine_get_version)(void); @@ -32,7 +36,7 @@ static LPCWSTR launch_via_steam_windows() { si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); - WCHAR steamPath[1024]; + WCHAR steamPath[1024] = L""; DWORD datasize = 1024; 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"; - if (CreateProcessW(steamPath, commandLine, - NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi) == 0) - return L"Could not launch Dwarf Fortress"; + BOOL res = CreateProcessW(steamPath, commandLine, + NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + + if (res) + { + WaitForSingleObject(pi.hProcess, INFINITE); - return NULL; + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return NULL; + } + else + { + return L"Could not launch Dwarf Fortress"; + } } -// this method doesn't properly attribute Steam playtime metrics to DF, -// but that's better than not having DF start at all. -static BOOL launch_direct() { +static LPCWSTR launch_direct() { STARTUPINFOW si; PROCESS_INFORMATION pi; @@ -60,31 +72,152 @@ static BOOL launch_direct() { si.cb = sizeof(si); 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); + + 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; + } + + do { + std::wstring executableName(entry.szExeFile); + if (executableName == L"Dwarf Fortress.exe") + { + CloseHandle(snapshot); + return entry.th32ProcessID; + } + } while (Process32NextW(snapshot, &entry)); + + CloseHandle(snapshot); + return -1; } -int WINAPI wWinMain(HINSTANCE hi, HINSTANCE hpi, PWSTR cmd, int ns) { +int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nShowCmd) { - if (SteamAPI_RestartAppIfNecessary(DFHACK_STEAM_APPID)) // Replace with your App ID + // initialize steam context + if (SteamAPI_RestartAppIfNecessary(DFHACK_STEAM_APPID)) { - return 1; + exit(0); } - if (!SteamAPI_Init()) + if (!SteamAPI_Init()) + { + // could not initialize steam context, attempt fallback launch + LPCWSTR err = launch_direct(); + if (err != NULL) { - printf("Fatal Error - Steam must be running to play this game (SteamAPI_Init() failed).\n"); - return 1; + MessageBoxW(NULL, err, NULL, 0); + exit(1); } + exit(0); + } - return 0; + bool wine = is_running_on_wine(); - LPCWSTR err = is_running_on_wine() ? launch_via_steam_posix() : launch_via_steam_windows(); + 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 && !launch_direct()) { - MessageBoxW(NULL, err, NULL, 0); + 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); + } + + // obtain DF app path + + char buf[2048] = ""; + + int b = SteamApps()->GetAppInstallDir(DFHACK_STEAM_APPID, (char*)&buf, 2048); + std::string dfhack_install_folder = (b != -1) ? std::string(buf) : ""; + + 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); + 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); }