예제 #1
0
        private static SteamGameData SearchAllInstallations(
            string libraryfoldersFile,
            uint appId)
        {
            if (!File.Exists(libraryfoldersFile))
            {
                return(null);
            }

            var steamLibraryPaths = GetLibraryPaths(File.ReadAllText(libraryfoldersFile));

            foreach (var steamLibraryPath in steamLibraryPaths)
            {
                var manifestFile = Path.Combine(steamLibraryPath, $"steamapps/appmanifest_{appId}.acf");
                if (!File.Exists(manifestFile))
                {
                    continue;
                }

                // Validate manifest is correct.
                SteamGameData game = GameDataFromAppManifest(manifestFile);
                if (game.Id != appId)
                {
                    continue;
                }

                return(game);
            }

            return(null);
        }
예제 #2
0
        public static SteamGameData TryFrom(string path)
        {
            try {
                var game = new SteamGameData();
                var xDoc = new XmlDocument();
                xDoc.Load(path);
                XmlNamespaceManager nsManager = new XmlNamespaceManager(xDoc.NameTable);
                nsManager.AddNamespace("d", xDoc.DocumentElement.NamespaceURI);
                foreach (XmlElement elem in xDoc.DocumentElement.SelectNodes("//d:PropertyGroup/*", nsManager))
                {
                    switch (elem.Name)
                    {
                    case "GameAppId":
                        game.Id = uint.Parse(elem.LastChild.Value);
                        break;

                    case "GameName":
                        game.Name = elem.LastChild.Value;
                        break;

                    case "GameFolderName":
                        game.InstallFolderName = elem.LastChild.Value;
                        break;

                    case "GameDir":
                        game.InstallDir = elem.LastChild.Value;
                        break;
                    }
                }
                return(game);
            } catch (Exception) {
                return(null);
            }
        }
예제 #3
0
        private static async Task EnsureUnityDoorstopAsync(SteamGameData game)
        {
            var zip = Path.Combine(Utils.GeneratedOutputDir, "unitydoorstop.zip");

            if (!File.Exists(zip))
            {
                using var client = new WebClient();
                await client.DownloadFileTaskAsync(UnityDoorstopDownloadUrl, zip);
            }

            // Extract UnityDoorstop zip over game files.
            using var zipReader = ZipFile.OpenRead(zip);

            string[] skipIfExists = { "doorstop_config.ini" };
            foreach (var entry in zipReader.Entries)
            {
                if (skipZipExtractContains.Any(
                        name => entry.FullName.IndexOf(name, StringComparison.InvariantCultureIgnoreCase) >= 0))
                {
                    continue;
                }
                var targetFile = Path.Combine(game.InstallDir, entry.FullName);
                if (File.Exists(targetFile) &&
                    skipIfExists.Any(f =>
                                     f.Equals(Path.GetFileName(targetFile), StringComparison.InvariantCultureIgnoreCase)))
                {
                    continue;
                }

                Directory.CreateDirectory(Path.GetDirectoryName(targetFile));
                entry.ExtractToFile(targetFile, true);
            }
        }
예제 #4
0
 static string ValidateUnityGame(SteamGameData game, uint steamAppId)
 {
     if (game.Id != steamAppId)
     {
         return($"Steam id in game.props {game.Id} does not match {steamAppId}");
     }
     if (!File.Exists(Path.Combine(game.InstallDir, "UnityPlayer.dll")))
     {
         return("Steam game is not a Unity game.");
     }
     if (!Directory.Exists(game.ManagedDllsDir))
     {
         throw new Exception("Game is missing Unity managed dlls directory.");
     }
     return(null);
 }
예제 #5
0
        public static async Task Main(string[] args)
        {
            if (SteamAppId <= 0)
            {
                throw new Exception("SteamAppId environment variable must be set and be valid");
            }

            Console.WriteLine($"Building mod for Steam game with id {SteamAppId}");
            SteamGameData game = await Task.Factory.StartNew(() => EnsureSteamGame(SteamAppId)).ConfigureAwait(false);

            Console.WriteLine($"Found game at {game.InstallDir}");
            await Task.WhenAll(
                Task.Factory.StartNew(() => EnsureBepInExAsync(game.InstallDir)).Unwrap(),
                Task.Factory.StartNew(() => EnsurePublicizedAssemblies(game)))
            .ConfigureAwait(false);
        }
예제 #6
0
        private static async Task EnsureUnstrippedMonoAssembliesAsync(SteamGameData game)
        {
            if (Directory.Exists(Path.Combine(game.InstallDir, UnstrippedDllsFolderName)))
            {
                Console.WriteLine("Unstripped Mono Assemblies are already installed.");
                return;
            }
            // Detect Unity Engine version the game is using.
            var unityVersionUsedByGame = new Version(FileVersionInfo
                                                     .GetVersionInfo(Path.Combine(game.InstallDir, "UnityPlayer.dll"))
                                                     .FileVersion).ToString(3);

            // Create Unstripped dlls directory in game directory
            var targetDir = Path.Combine(game.InstallDir, UnstrippedDllsFolderName);

            Directory.CreateDirectory(targetDir);

            // Download unstripped dlls
            var dllsZip = Path.Combine(Utils.GeneratedOutputDir, $@"{unityVersionUsedByGame}.zip");

            if (!File.Exists(dllsZip))
            {
                using var client = new WebClient();
                await client.DownloadFileTaskAsync(
                    UnityUnstrippedDllsRepo.Replace("{VERSION}", unityVersionUsedByGame),
                    dllsZip);
            }

            // Extract unstripped dlls to unstripped dlls directory in game directory
            using var zipReader = ZipFile.OpenRead(dllsZip);
            foreach (var entry in zipReader.Entries)
            {
                var fileName = Path.GetFileName(entry.FullName);
                if (string.IsNullOrWhiteSpace(fileName))
                {
                    continue;
                }

                var targetFile = Path.Combine(targetDir, fileName);
                entry.ExtractToFile(targetFile, true);
            }
        }
예제 #7
0
        private static void EnsurePublicizedAssemblies(SteamGameData game)
        {
            if (SkipPublicizer)
            {
                Console.WriteLine("Skipping Assembly Publicizer, execute this manually.");
                return;
            }
            if (Directory.Exists(Path.Combine(Utils.GeneratedOutputDir, "publicized_assemblies")))
            {
                Console.WriteLine("Assemblies are already publicized.");
                return;
            }

            var dllsToPublicize = Directory.GetFiles(game.ManagedDllsDir, "assembly_*.dll");

            foreach (var publicizedDll in Publicizer.Execute(dllsToPublicize,
                                                             "_publicized",
                                                             Path.Combine(Utils.GeneratedOutputDir, "publicized_assemblies")))
            {
                Console.WriteLine($"Wrote publicized dll: {publicizedDll}");
            }
        }
예제 #8
0
        /// <summary>
        ///     Finds game install directory by iterating through all the steam game libraries configured and finding the appid
        ///     that matches.
        /// </summary>
        /// <param name="steamAppId"></param>
        /// <returns></returns>
        /// <exception cref="Exception">If steam is not installed or game could not be found.</exception>
        public static SteamGameData FindGame(uint steamAppId)
        {
            var steamPath = (string)ReadRegistrySafe("Software\\Valve\\Steam", "SteamPath");

            if (string.IsNullOrEmpty(steamPath))
            {
                try
                {
                    steamPath = (string)ReadRegistrySafe(@"SOFTWARE\Valve\Steam",
                                                         "InstallPath",
                                                         RegistryHive.LocalMachine);
                }
                finally
                {
                    if (string.IsNullOrEmpty(steamPath))
                    {
                        throw new Exception("Steam could not be found. Check if it is installed.");
                    }
                }
            }

            var appsPath = Path.Combine(steamPath, "steamapps");

            // Test main steamapps.
            SteamGameData game = GameDataFromAppManifest(Path.Combine(appsPath, $"appmanifest_{steamAppId}.acf"));

            if (game == null)
            {
                // Test steamapps on other drives (as defined by Steam).
                game = SearchAllInstallations(Path.Combine(appsPath, "libraryfolders.vdf"), steamAppId);
                if (game == null)
                {
                    throw new Exception($"Steam game with id {steamAppId} is not installed.");
                }
            }

            return(game);
        }
예제 #9
0
        private static void EnsureUnityDoorstopConfig(SteamGameData game)
        {
            // Change UnityDoorstop configuration to make it override game dlls with unstripped dlls.
            var unityDoorstopConfig = Path.Combine(game.InstallDir, "doorstop_config.ini");

            if (!File.Exists(unityDoorstopConfig))
            {
                throw new FileNotFoundException("UnityDoorstop config file not found", unityDoorstopConfig);
            }
            var configContent  = File.ReadAllLines(unityDoorstopConfig).ToList();
            var requiredValues = new Dictionary <string, string>
            {
                { "dllSearchPathOverride", UnstrippedDllsFolderName },
                { "targetAssembly", @"BepInEx\core\BepInEx.Preloader.dll" }
            };

            for (var i = 0; i < configContent.Count; i++)
            {
                var match = iniRegex.Match(configContent[i]);
                if (!match.Success)
                {
                    continue;
                }
                if (!requiredValues.TryGetValue(match.Groups[1].Value, out var value))
                {
                    continue;
                }

                configContent[i] = $"{match.Groups[1].Value}={value}";
                requiredValues.Remove(match.Groups[1].Value);
            }
            foreach (var pair in requiredValues)
            {
                configContent.Add($"{pair.Key}={pair.Value}");
            }
            File.WriteAllLines(unityDoorstopConfig, configContent);
        }