在目录 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}";
            }
        }
    }
}