【问题标题】:Finding game launcher executables in directory C#在目录 C# 中查找游戏启动器可执行文件
【发布时间】:2019-07-13 00:58:04
【问题描述】:

我正在尝试查找游戏的可执行文件;但是有些是嵌套的(例如 Ark: Survival Evolved)(A:\Steam Games\steamapps\common\ARK\ShooterGame\Binaries\Win64\ShooterGame.exe

我花了很长时间试图找到一种方法来只找到相关的可执行文件。

This link 在我们不递归搜索时显示游戏。它会找到大多数,但不是所有的 .exe 文件

This link 显示递归搜索游戏,但也显示一堆二进制文件/redist exe。

最初我尝试排除“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++;
                }
            }

        }
    }

【问题讨论】:

  • 没有标准化的方法来确定哪些可执行文件是“正确”的。有时它们将整个游戏包含在二进制文件中,有时它们只是一个非常薄的包装器。通常的解决方案是拥有一个包含 exe 名称的数据库。您可以尝试从现有来源获取这些数据(我想到了显卡驱动程序的配置文件),但除此之外,您别无选择,只能维护自己的映射。
  • 我不明白这怎么可能。您可以移动您的 Steam 文件夹。您可以完全将磁盘安装到其他地方。你怎么知道哪个exe是游戏?例如。我的 winspww2 副本位于 E:\WinspWW2
  • 它存储在 appcache\appinfo.vdf 中:每个游戏都有一个条目和一个“可执行”属性。但是,它以二进制格式存储,您需要取消选择。那里已经有一些代码,例如the Python steamfiles module
  • 谢谢@Rup,我得调查一下。 Andy,steam 文件夹存储在 library.vdf 中,因此我们可以找到游戏的存储位置,而不是游戏的 .exe 文件(Rup 提供了帮助)。 Lennart 是的,我是这么认为的,我也会看一下,很可能会退回到用户选择正确的那个。 Liam 好吧,这是一个启动游戏的文件.. 所以

标签: c# exe executable steam


【解决方案1】:

keydata 似乎在 appinfo.vdf 文件中。因此,为 Steam 游戏获取正确 EXE 文件的一些工作代码如下。如果游戏是带有指向 URL 的指针的 EA 游戏,或者 appinfo.vdf 中没有数据,则可以使用回退到您之前的解决方案。如果有人能很好地解析 appinfo.vdf,那么代码会更好,而且不像“IndexOf”解决方案那样老套。

以下代码中的 GetSteamPath() 可以更新为从注册表中获取。

我将使用该代码来修复那些回到那个匿名地球网址图标的琐碎的蒸汽链接。

(用 .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}";
            }
        }
    }
}

【讨论】:

    【解决方案2】:

    我已经尽力做到了,所以首先我通过注册表检测 Steam 的安装位置,然后我检查 /steamapps/libraryfolders.vdf 以了解用户库的位置,然后检查这些库是否有任何“顶级" 可执行文件。如果在顶层目录中没有找到,该程序会让用户选择他们自己的 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\\");
                }
            }
        }
    }
    

    附上代码,以便其他人可以看到我是如何做到的。我知道这不是最好的,但它正在工作

    【讨论】:

    • 查看相同的内容。似乎下一个线索在于 steamapps\appmanifest_appid.acf,它又具有指向 \depotcache\appiddepotid_manifestid.manifest 的指针,其中包含一些二进制形式的元数据。
    猜你喜欢
    • 1970-01-01
    • 2020-10-29
    • 1970-01-01
    • 1970-01-01
    • 2012-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多