在目录 C# 中查找游戏启动器可执行文件
Finding game launcher executables in directory C#
我正在寻找游戏的可执行文件;但是有些是嵌套的(例如方舟:生存进化)(A:\Steam Games\steamapps\common\ARK\ShooterGame\Binaries\Win64\ShooterGame.exe)
我花了很长时间试图找到一种方法来只找到相关的可执行文件。
This link 显示我们不递归搜索时的游戏。它找到了大部分,但不是所有的 .exe
This link 显示游戏递归搜索,但也显示一堆 binaries/redist exes。
最初我尝试排除 "bin"、"binary"、"binaries"、"redist" 文件夹,但那并没有给我例如方舟:生存进化。
我还考虑过根据大小过滤 .exe,但 Aperture Tag 的 QC_Eyes.exe 为 3055KB,而古墓丽影 II.exe 为 892KB .
这是我用来查找 Steam 安装目录的方法,并检查 libraryfolders.vdf 文件中库的位置。现在我只是写到控制台,这样我就可以看到输出是什么。
如果有人对我如何为正确的游戏找到正确的文件有任何提示,我将不胜感激。谢谢
public void SearchSteam()
{
steamGameDirs.Clear();
string steam32 = "SOFTWARE\VALVE\";
string steam64 = "SOFTWARE\Wow6432Node\Valve\";
string steam32path;
string steam64path;
string config32path;
string config64path;
RegistryKey key32 = Registry.LocalMachine.OpenSubKey(steam32);
RegistryKey key64 = Registry.LocalMachine.OpenSubKey(steam64);
foreach(string k32subKey in key32.GetSubKeyNames())
{
using (RegistryKey subKey = key32.OpenSubKey(k32subKey))
{
steam32path = subKey.GetValue("InstallPath").ToString();
config32path = steam32path + "/steamapps/libraryfolders.vdf";
if (File.Exists(config32path))
{
string[] configLines = File.ReadAllLines(config32path);
foreach(var item in configLines)
{
Console.WriteLine("32: " + item);
}
}
}
}
foreach(string k64subKey in key64.GetSubKeyNames())
{
using (RegistryKey subKey = key64.OpenSubKey(k64subKey))
{
steam64path = subKey.GetValue("InstallPath").ToString();
config64path = steam64path + "/steamapps/libraryfolders.vdf";
string driveRegex = @"[A-Z]:\";
if (File.Exists(config64path))
{
string[] configLines = File.ReadAllLines(config64path);
foreach (var item in configLines)
{
Console.WriteLine("64: " + item);
Match match = Regex.Match(item, driveRegex);
if(item != string.Empty && match.Success)
{
string matched = match.ToString();
string item2 = item.Substring(item.IndexOf(matched));
item2 = item2.Replace("\\", "\");
steamGameDirs.Add(item2);
}
}
steamGameDirs.Add(steam64path + "\steamapps\common\");
}
}
}
foreach(string item in steamGameDirs)
{
string GameTitle;
string[] Executables = new string[0];
string[] steamGames = Directory.GetDirectories(item);
foreach (var dir in steamGames)
{
string title = dir.Substring(dir.IndexOf("\common\"));
string[] titlex = title.Split('\');
title = titlex[2].ToString();
GameTitle = title;
Console.WriteLine("Title: " + GameTitle);
Console.WriteLine("Directory: " + dir);
string[] executables = Directory.GetFiles(dir, "*.exe", SearchOption.AllDirectories);
int num = 0;
foreach (var ex in executables)
{
//add "ex" to Executables[] if poss
Console.WriteLine(ex);
num++;
}
}
}
}
我已尽我所能,所以首先我通过注册表检测安装了 Steam 的位置,然后我检查 /steamapps/libraryfolders.vdf 以了解用户库的位置,然后检查这些库是否有任何"top level" 个可执行文件。然后,如果在顶级目录中找不到,该程序会让用户 select 他们自己的 exe。
似乎是目前最好的解决方案,因为 steamfiles 模块当前未激活。
public void SearchSteam()
{
steamGameDirs.Clear();
string steam32 = "SOFTWARE\VALVE\";
string steam64 = "SOFTWARE\Wow6432Node\Valve\";
string steam32path;
string steam64path;
string config32path;
string config64path;
RegistryKey key32 = Registry.LocalMachine.OpenSubKey(steam32);
RegistryKey key64 = Registry.LocalMachine.OpenSubKey(steam64);
if (key64.ToString() == null || key64.ToString() == "")
{
foreach (string k32subKey in key32.GetSubKeyNames())
{
using (RegistryKey subKey = key32.OpenSubKey(k32subKey))
{
steam32path = subKey.GetValue("InstallPath").ToString();
config32path = steam32path + "/steamapps/libraryfolders.vdf";
string driveRegex = @"[A-Z]:\";
if (File.Exists(config32path))
{
string[] configLines = File.ReadAllLines(config32path);
foreach (var item in configLines)
{
Console.WriteLine("32: " + item);
Match match = Regex.Match(item, driveRegex);
if (item != string.Empty && match.Success)
{
string matched = match.ToString();
string item2 = item.Substring(item.IndexOf(matched));
item2 = item2.Replace("\\", "\");
item2 = item2.Replace("\"", "\steamapps\common\");
steamGameDirs.Add(item2);
}
}
steamGameDirs.Add(steam32path + "\steamapps\common\");
}
}
}
}
foreach(string k64subKey in key64.GetSubKeyNames())
{
using (RegistryKey subKey = key64.OpenSubKey(k64subKey))
{
steam64path = subKey.GetValue("InstallPath").ToString();
config64path = steam64path + "/steamapps/libraryfolders.vdf";
string driveRegex = @"[A-Z]:\";
if (File.Exists(config64path))
{
string[] configLines = File.ReadAllLines(config64path);
foreach (var item in configLines)
{
Console.WriteLine("64: " + item);
Match match = Regex.Match(item, driveRegex);
if(item != string.Empty && match.Success)
{
string matched = match.ToString();
string item2 = item.Substring(item.IndexOf(matched));
item2 = item2.Replace("\\", "\");
item2 = item2.Replace("\"", "\steamapps\common\");
steamGameDirs.Add(item2);
}
}
steamGameDirs.Add(steam64path + "\steamapps\common\");
}
}
}
}
附上代码,以便其他人可以看到我是如何做到的。我知道这不是最好的,但它很有效
密钥数据似乎在 appinfo.vdf 文件中。因此,为 Steam 游戏获取正确的 EXE 文件的一些工作代码如下。如果游戏是带有指向 URL 指针的 EA 游戏,或者 appinfo.vdf 中没有数据,则可以使用您之前的解决方案的回退。如果有人得到了 appinfo.vdf 的良好解析器,代码会更好,而且不会像“IndexOf”解决方案那样笨拙。
下面代码中的 GetSteamPath() 可以更新为从注册表中获取。
我将使用该代码修复那些回到那个匿名地球-url 图标的琐碎的 Steam 链接。
(用 .Net 5.0 编写 - 似乎 .4.7.2 的 IndexOf 很古怪。我认为不喜欢 \x00)
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
namespace SteamAppsParser
{
class Program
{
static void Main(string[] args)
{
var libs = GetSteamLibs();
var apps = GetSteamApps(libs);
}
static List<AppInfo> GetSteamApps(List<string> steamLibs)
{
var apps = new List<AppInfo>();
foreach (var lib in steamLibs)
{
var appMetaDataPath = Path.Combine(lib, "SteamApps");
var files = Directory.GetFiles(appMetaDataPath, "*.acf");
foreach (var file in files)
{
var appInfo = GetAppInfo(file);
if (appInfo != null)
{
apps.Add(appInfo);
}
}
}
return apps;
}
static AppInfo GetAppInfo(string appMetaFile)
{
var fileDataLines = File.ReadAllLines(appMetaFile);
var dic = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var line in fileDataLines)
{
var match = Regex.Match(line, @"\s*""(?<key>\w+)""\s+""(?<val>.*)""");
if (match.Success)
{
var key = match.Groups["key"].Value;
var val = match.Groups["val"].Value;
dic[key] = val;
}
}
AppInfo appInfo = null;
if (dic.Keys.Count > 0)
{
appInfo = new AppInfo();
var appId = dic["appid"];
var name = dic["name"];
var installDir = dic["installDir"];
var path = Path.GetDirectoryName(appMetaFile);
var libGameRoot = Path.Combine(path, "common", installDir);
if (!Directory.Exists(libGameRoot)) return null;
appInfo.Id = appId;
appInfo.Name = name;
appInfo.Manifest = appMetaFile;
appInfo.GameRoot = libGameRoot;
appInfo.InstallDir = installDir;
appInfo.SteamUrl = $"steam://runsteamid/{appId}";
//if (appInfo.Name.StartsWith("Sid Meier"))
appInfo.Executable = GetExecutable(appInfo);
}
return appInfo;
}
static string _appInfoText = null;
static string GetExecutable(AppInfo appInfo)
{
if (_appInfoText == null)
{
var appInfoFile = Path.Combine(GetSteamPath(), "appcache", "appinfo.vdf");
var bytes = File.ReadAllBytes(appInfoFile);
_appInfoText = Encoding.UTF8.GetString(bytes);
}
var startIndex = 0;
int maxTries = 50;
var fullName = "";
do
{
var startOfDataArea = _appInfoText.IndexOf($"\x00\x01name\x00{appInfo.Name}\x00", startIndex);
if (startOfDataArea < 0 && maxTries == 50) startOfDataArea = _appInfoText.IndexOf($"\x00\x01gamedir\x00{appInfo.Name}\x00", startIndex); //Alternative1
if (startOfDataArea < 0 && maxTries == 50) startOfDataArea = _appInfoText.IndexOf($"\x00\x01name\x00{appInfo.Name}\x00", startIndex); //Alternative2
if (startOfDataArea > 0)
{
startIndex = startOfDataArea + 10;
int nextLaunch = -1;
do
{
var executable = _appInfoText.IndexOf($"\x00\x01executable\x00", startOfDataArea);
if (executable>-1 && nextLaunch == -1)
{
nextLaunch = _appInfoText.IndexOf($"\x00\x01launch\x00", executable);
}
if ((nextLaunch <= 0 || executable < nextLaunch) && executable > 0)
{
if (executable > 0)
{
executable += 10;
string filename = "";
while (_appInfoText[executable] != '\x00')
{
filename += _appInfoText[executable];
executable++;
}
if (filename.Contains("://"))
{
//EA or other external
return filename; //Need to use other means to grab the EXE here.
}
fullName = Path.Combine(appInfo.GameRoot, filename);
startOfDataArea = executable + 1;
startIndex = startOfDataArea + 10;
}
}
else
{
break;
}
}
while (!File.Exists(fullName) && maxTries-- > 0);
}
else
{
return null;
}
} while (!File.Exists(fullName) && maxTries-- > 0);
if (File.Exists(fullName)) return fullName;
return null;
}
static List<string> GetSteamLibs()
{
var steamPath = GetSteamPath();
var libraries = new List<string>() { steamPath };
var listFile = Path.Combine(steamPath, @"steamapps\libraryfolders.vdf");
var lines = File.ReadAllLines(listFile);
foreach (var line in lines)
{
var match = Regex.Match(line, @"""(?<path>\w:\\.*)""");
if (match.Success)
{
var path = match.Groups["path"].Value.Replace(@"\", @"\");
if (Directory.Exists(path))
{
libraries.Add(path);
}
}
}
return libraries;
}
static string GetSteamPath()
{
return @"C:\Spill\Steam";
}
class AppInfo
{
public string Id { get; internal set; }
public string Name { get; internal set; }
public string SteamUrl { get; internal set; }
public string Manifest { get; internal set; }
public string GameRoot { get; internal set; }
public string Executable { get; internal set; }
public string InstallDir { get; internal set; }
public override string ToString()
{
return $"{Name} ({Id}) - {SteamUrl} - {Executable}";
}
}
}
}
我正在寻找游戏的可执行文件;但是有些是嵌套的(例如方舟:生存进化)(A:\Steam Games\steamapps\common\ARK\ShooterGame\Binaries\Win64\ShooterGame.exe)
我花了很长时间试图找到一种方法来只找到相关的可执行文件。
This link 显示我们不递归搜索时的游戏。它找到了大部分,但不是所有的 .exe
This link 显示游戏递归搜索,但也显示一堆 binaries/redist exes。
最初我尝试排除 "bin"、"binary"、"binaries"、"redist" 文件夹,但那并没有给我例如方舟:生存进化。
我还考虑过根据大小过滤 .exe,但 Aperture Tag 的 QC_Eyes.exe 为 3055KB,而古墓丽影 II.exe 为 892KB .
这是我用来查找 Steam 安装目录的方法,并检查 libraryfolders.vdf 文件中库的位置。现在我只是写到控制台,这样我就可以看到输出是什么。
如果有人对我如何为正确的游戏找到正确的文件有任何提示,我将不胜感激。谢谢
public void SearchSteam()
{
steamGameDirs.Clear();
string steam32 = "SOFTWARE\VALVE\";
string steam64 = "SOFTWARE\Wow6432Node\Valve\";
string steam32path;
string steam64path;
string config32path;
string config64path;
RegistryKey key32 = Registry.LocalMachine.OpenSubKey(steam32);
RegistryKey key64 = Registry.LocalMachine.OpenSubKey(steam64);
foreach(string k32subKey in key32.GetSubKeyNames())
{
using (RegistryKey subKey = key32.OpenSubKey(k32subKey))
{
steam32path = subKey.GetValue("InstallPath").ToString();
config32path = steam32path + "/steamapps/libraryfolders.vdf";
if (File.Exists(config32path))
{
string[] configLines = File.ReadAllLines(config32path);
foreach(var item in configLines)
{
Console.WriteLine("32: " + item);
}
}
}
}
foreach(string k64subKey in key64.GetSubKeyNames())
{
using (RegistryKey subKey = key64.OpenSubKey(k64subKey))
{
steam64path = subKey.GetValue("InstallPath").ToString();
config64path = steam64path + "/steamapps/libraryfolders.vdf";
string driveRegex = @"[A-Z]:\";
if (File.Exists(config64path))
{
string[] configLines = File.ReadAllLines(config64path);
foreach (var item in configLines)
{
Console.WriteLine("64: " + item);
Match match = Regex.Match(item, driveRegex);
if(item != string.Empty && match.Success)
{
string matched = match.ToString();
string item2 = item.Substring(item.IndexOf(matched));
item2 = item2.Replace("\\", "\");
steamGameDirs.Add(item2);
}
}
steamGameDirs.Add(steam64path + "\steamapps\common\");
}
}
}
foreach(string item in steamGameDirs)
{
string GameTitle;
string[] Executables = new string[0];
string[] steamGames = Directory.GetDirectories(item);
foreach (var dir in steamGames)
{
string title = dir.Substring(dir.IndexOf("\common\"));
string[] titlex = title.Split('\');
title = titlex[2].ToString();
GameTitle = title;
Console.WriteLine("Title: " + GameTitle);
Console.WriteLine("Directory: " + dir);
string[] executables = Directory.GetFiles(dir, "*.exe", SearchOption.AllDirectories);
int num = 0;
foreach (var ex in executables)
{
//add "ex" to Executables[] if poss
Console.WriteLine(ex);
num++;
}
}
}
}
我已尽我所能,所以首先我通过注册表检测安装了 Steam 的位置,然后我检查 /steamapps/libraryfolders.vdf 以了解用户库的位置,然后检查这些库是否有任何"top level" 个可执行文件。然后,如果在顶级目录中找不到,该程序会让用户 select 他们自己的 exe。
似乎是目前最好的解决方案,因为 steamfiles 模块当前未激活。
public void SearchSteam()
{
steamGameDirs.Clear();
string steam32 = "SOFTWARE\VALVE\";
string steam64 = "SOFTWARE\Wow6432Node\Valve\";
string steam32path;
string steam64path;
string config32path;
string config64path;
RegistryKey key32 = Registry.LocalMachine.OpenSubKey(steam32);
RegistryKey key64 = Registry.LocalMachine.OpenSubKey(steam64);
if (key64.ToString() == null || key64.ToString() == "")
{
foreach (string k32subKey in key32.GetSubKeyNames())
{
using (RegistryKey subKey = key32.OpenSubKey(k32subKey))
{
steam32path = subKey.GetValue("InstallPath").ToString();
config32path = steam32path + "/steamapps/libraryfolders.vdf";
string driveRegex = @"[A-Z]:\";
if (File.Exists(config32path))
{
string[] configLines = File.ReadAllLines(config32path);
foreach (var item in configLines)
{
Console.WriteLine("32: " + item);
Match match = Regex.Match(item, driveRegex);
if (item != string.Empty && match.Success)
{
string matched = match.ToString();
string item2 = item.Substring(item.IndexOf(matched));
item2 = item2.Replace("\\", "\");
item2 = item2.Replace("\"", "\steamapps\common\");
steamGameDirs.Add(item2);
}
}
steamGameDirs.Add(steam32path + "\steamapps\common\");
}
}
}
}
foreach(string k64subKey in key64.GetSubKeyNames())
{
using (RegistryKey subKey = key64.OpenSubKey(k64subKey))
{
steam64path = subKey.GetValue("InstallPath").ToString();
config64path = steam64path + "/steamapps/libraryfolders.vdf";
string driveRegex = @"[A-Z]:\";
if (File.Exists(config64path))
{
string[] configLines = File.ReadAllLines(config64path);
foreach (var item in configLines)
{
Console.WriteLine("64: " + item);
Match match = Regex.Match(item, driveRegex);
if(item != string.Empty && match.Success)
{
string matched = match.ToString();
string item2 = item.Substring(item.IndexOf(matched));
item2 = item2.Replace("\\", "\");
item2 = item2.Replace("\"", "\steamapps\common\");
steamGameDirs.Add(item2);
}
}
steamGameDirs.Add(steam64path + "\steamapps\common\");
}
}
}
}
附上代码,以便其他人可以看到我是如何做到的。我知道这不是最好的,但它很有效
密钥数据似乎在 appinfo.vdf 文件中。因此,为 Steam 游戏获取正确的 EXE 文件的一些工作代码如下。如果游戏是带有指向 URL 指针的 EA 游戏,或者 appinfo.vdf 中没有数据,则可以使用您之前的解决方案的回退。如果有人得到了 appinfo.vdf 的良好解析器,代码会更好,而且不会像“IndexOf”解决方案那样笨拙。
下面代码中的 GetSteamPath() 可以更新为从注册表中获取。
我将使用该代码修复那些回到那个匿名地球-url 图标的琐碎的 Steam 链接。
(用 .Net 5.0 编写 - 似乎 .4.7.2 的 IndexOf 很古怪。我认为不喜欢 \x00)
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
namespace SteamAppsParser
{
class Program
{
static void Main(string[] args)
{
var libs = GetSteamLibs();
var apps = GetSteamApps(libs);
}
static List<AppInfo> GetSteamApps(List<string> steamLibs)
{
var apps = new List<AppInfo>();
foreach (var lib in steamLibs)
{
var appMetaDataPath = Path.Combine(lib, "SteamApps");
var files = Directory.GetFiles(appMetaDataPath, "*.acf");
foreach (var file in files)
{
var appInfo = GetAppInfo(file);
if (appInfo != null)
{
apps.Add(appInfo);
}
}
}
return apps;
}
static AppInfo GetAppInfo(string appMetaFile)
{
var fileDataLines = File.ReadAllLines(appMetaFile);
var dic = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var line in fileDataLines)
{
var match = Regex.Match(line, @"\s*""(?<key>\w+)""\s+""(?<val>.*)""");
if (match.Success)
{
var key = match.Groups["key"].Value;
var val = match.Groups["val"].Value;
dic[key] = val;
}
}
AppInfo appInfo = null;
if (dic.Keys.Count > 0)
{
appInfo = new AppInfo();
var appId = dic["appid"];
var name = dic["name"];
var installDir = dic["installDir"];
var path = Path.GetDirectoryName(appMetaFile);
var libGameRoot = Path.Combine(path, "common", installDir);
if (!Directory.Exists(libGameRoot)) return null;
appInfo.Id = appId;
appInfo.Name = name;
appInfo.Manifest = appMetaFile;
appInfo.GameRoot = libGameRoot;
appInfo.InstallDir = installDir;
appInfo.SteamUrl = $"steam://runsteamid/{appId}";
//if (appInfo.Name.StartsWith("Sid Meier"))
appInfo.Executable = GetExecutable(appInfo);
}
return appInfo;
}
static string _appInfoText = null;
static string GetExecutable(AppInfo appInfo)
{
if (_appInfoText == null)
{
var appInfoFile = Path.Combine(GetSteamPath(), "appcache", "appinfo.vdf");
var bytes = File.ReadAllBytes(appInfoFile);
_appInfoText = Encoding.UTF8.GetString(bytes);
}
var startIndex = 0;
int maxTries = 50;
var fullName = "";
do
{
var startOfDataArea = _appInfoText.IndexOf($"\x00\x01name\x00{appInfo.Name}\x00", startIndex);
if (startOfDataArea < 0 && maxTries == 50) startOfDataArea = _appInfoText.IndexOf($"\x00\x01gamedir\x00{appInfo.Name}\x00", startIndex); //Alternative1
if (startOfDataArea < 0 && maxTries == 50) startOfDataArea = _appInfoText.IndexOf($"\x00\x01name\x00{appInfo.Name}\x00", startIndex); //Alternative2
if (startOfDataArea > 0)
{
startIndex = startOfDataArea + 10;
int nextLaunch = -1;
do
{
var executable = _appInfoText.IndexOf($"\x00\x01executable\x00", startOfDataArea);
if (executable>-1 && nextLaunch == -1)
{
nextLaunch = _appInfoText.IndexOf($"\x00\x01launch\x00", executable);
}
if ((nextLaunch <= 0 || executable < nextLaunch) && executable > 0)
{
if (executable > 0)
{
executable += 10;
string filename = "";
while (_appInfoText[executable] != '\x00')
{
filename += _appInfoText[executable];
executable++;
}
if (filename.Contains("://"))
{
//EA or other external
return filename; //Need to use other means to grab the EXE here.
}
fullName = Path.Combine(appInfo.GameRoot, filename);
startOfDataArea = executable + 1;
startIndex = startOfDataArea + 10;
}
}
else
{
break;
}
}
while (!File.Exists(fullName) && maxTries-- > 0);
}
else
{
return null;
}
} while (!File.Exists(fullName) && maxTries-- > 0);
if (File.Exists(fullName)) return fullName;
return null;
}
static List<string> GetSteamLibs()
{
var steamPath = GetSteamPath();
var libraries = new List<string>() { steamPath };
var listFile = Path.Combine(steamPath, @"steamapps\libraryfolders.vdf");
var lines = File.ReadAllLines(listFile);
foreach (var line in lines)
{
var match = Regex.Match(line, @"""(?<path>\w:\\.*)""");
if (match.Success)
{
var path = match.Groups["path"].Value.Replace(@"\", @"\");
if (Directory.Exists(path))
{
libraries.Add(path);
}
}
}
return libraries;
}
static string GetSteamPath()
{
return @"C:\Spill\Steam";
}
class AppInfo
{
public string Id { get; internal set; }
public string Name { get; internal set; }
public string SteamUrl { get; internal set; }
public string Manifest { get; internal set; }
public string GameRoot { get; internal set; }
public string Executable { get; internal set; }
public string InstallDir { get; internal set; }
public override string ToString()
{
return $"{Name} ({Id}) - {SteamUrl} - {Executable}";
}
}
}
}