diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f079906 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# Autosave files +*~ + +# build +[Oo]bj/ +[Bb]in/ +TestResults/ + +# globs +Makefile.in +*.DS_Store +*.sln.cache +*.suo +*.cache +*.pidb +*.userprefs +*.usertasks +config.log +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.user +*.tar.gz +tarballs/ +test-results/ +Thumbs.db +.vs/ +packages/ + +# Mac bundle stuff +*.dmg +*.app + +# resharper +*_Resharper.* +*.Resharper + +# dotCover +*.dotCover diff --git a/CleanAndLaunch/App.config b/CleanAndLaunch/App.config new file mode 100644 index 0000000..b50c74f --- /dev/null +++ b/CleanAndLaunch/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CleanAndLaunch/CleanAndLaunch.csproj b/CleanAndLaunch/CleanAndLaunch.csproj new file mode 100644 index 0000000..af73f52 --- /dev/null +++ b/CleanAndLaunch/CleanAndLaunch.csproj @@ -0,0 +1,52 @@ + + + + + Debug + AnyCPU + {8E7856FD-FA68-4356-9C88-54C9EF5A9D20} + Exe + CleanAndLaunch + CleanAndLaunch + v4.6.2 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CleanAndLaunch/Program.cs b/CleanAndLaunch/Program.cs new file mode 100644 index 0000000..56ceaf7 --- /dev/null +++ b/CleanAndLaunch/Program.cs @@ -0,0 +1,120 @@ +/* + * Natural Launcher + +Copyright (C) 2018 Mael "Khelben" Vignaux + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact me with any question at the mail : mael.vignaux@elseware-experience.com +*/ +using System; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Timers; + +// The goal of this application is just to clean old files in the system and launch the new launcher... EZ RIGHT ? The app is called after the self updater included in the whole NaturalLauncher. +// This is not used anymore. Meh ! +namespace CleanAndLaunch +{ + class Program + { + public static string curDir = Directory.GetCurrentDirectory(); + public static string ProcessName = "NaturalLauncher.exe"; + + static void Main(string[] args) + { + Thread.Sleep(1000); + DeleteOldFiles(); + + System.Timers.Timer ReLaunchTimer = new System.Timers.Timer(); + ReLaunchTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent); + ReLaunchTimer.Interval = 100; + ReLaunchTimer.Enabled = true; + Console.Read(); + } + + public static void OnTimedEvent(object source, ElapsedEventArgs e) + { + if (IsProcessOpen(ProcessName)==false) + { + Launch(); + } + } + + static void Launch() + { + Console.WriteLine("Launching..."); + + Process myprocess = new Process(); + myprocess.StartInfo.FileName = curDir + Path.DirectorySeparatorChar + ProcessName; + try + { + ProcessStartInfo processInfo = new ProcessStartInfo(); + processInfo.WorkingDirectory = curDir; + processInfo.FileName = ProcessName; + processInfo.ErrorDialog = true; + processInfo.UseShellExecute = false; + processInfo.RedirectStandardOutput = true; + processInfo.RedirectStandardError = true; + Process proc = Process.Start(processInfo); + Environment.Exit(0); + } + catch(Exception e) + { + Console.WriteLine(e.ToString()); + Console.ReadKey(); + } + + } + + public static bool IsProcessOpen(string name) + { + foreach (Process clsProcess in Process.GetProcesses()) + { + if (clsProcess.ProcessName.Contains(name)) + { + return true; + } + } + + return false; + } + + public static void DeleteOldFiles() + { + Console.WriteLine("Cleaning old and update files ..."); + foreach (string file in Directory.GetFiles(curDir)) + { + // Cleaning BAK files + if (Path.GetExtension(file) == ".bak") + { + File.Delete(file); + } + string localPath = ToLocalPath(curDir, file); + + // Cleaning UpdateLogs files + if (localPath.StartsWith("/UpdateLog")) + { + File.Delete(file); + } + } + } + + public static string ToLocalPath(string root, string dir) + { + return dir.Replace(root, "").Replace("\\", "/"); + } + } +} diff --git a/CleanAndLaunch/Properties/AssemblyInfo.cs b/CleanAndLaunch/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..cbe63a8 --- /dev/null +++ b/CleanAndLaunch/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Les informations générales relatives à un assembly dépendent de +// l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations +// associées à un assembly. +[assembly: AssemblyTitle("CleanAndLaunch")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CleanAndLaunch")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly +// aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de +// COM, affectez la valeur true à l'attribut ComVisible sur ce type. +[assembly: ComVisible(false)] + +// Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM +[assembly: Guid("8e7856fd-fa68-4356-9c88-54c9ef5a9d20")] + +// Les informations de version pour un assembly se composent des quatre valeurs suivantes : +// +// Version principale +// Version secondaire +// Numéro de build +// Révision +// +// Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut +// en utilisant '*', comme indiqué ci-dessous : +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/LICENSE b/LICENSE index d4fa112..1573be2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,16 +1,15 @@ -MIT License +This programm is an updater to keep your Natural Selection 1 mod folder up with the latest updates. -Copyright (c) 2018 ENSL +Copyright (C) <2018> -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. Commercial use is prohibited. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -19,3 +18,9 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact me with any question at the mail : mael.vignaux@elseware-experience.com \ No newline at end of file diff --git a/ManifestBuilder/App.config b/ManifestBuilder/App.config new file mode 100644 index 0000000..b50c74f --- /dev/null +++ b/ManifestBuilder/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/ManifestBuilder/LauncherManifest.cs b/ManifestBuilder/LauncherManifest.cs new file mode 100644 index 0000000..5293b00 --- /dev/null +++ b/ManifestBuilder/LauncherManifest.cs @@ -0,0 +1,40 @@ +/* + * Natural Launcher + +Copyright (C) 2018 Mael "Khelben" Vignaux + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact me with any question at the mail : mael.vignaux@elseware-experience.com +*/ +using System.Collections.Generic; + +namespace ManifestBuilder +{ + class LauncherManifest + { + public LauncherManifest() + { + Files = new Dictionary(); + } + + public Dictionary Files + { + get; + set; + } + + } +} + diff --git a/ManifestBuilder/ManifestBuilder.csproj b/ManifestBuilder/ManifestBuilder.csproj new file mode 100644 index 0000000..6711389 --- /dev/null +++ b/ManifestBuilder/ManifestBuilder.csproj @@ -0,0 +1,58 @@ + + + + + Debug + AnyCPU + {6A7DBE40-6BFB-49F5-BFC1-9F0752EC31BE} + Exe + ManifestBuilder + ManifestBuilder + v4.6.2 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ManifestBuilder/ManifestBuildor.cs b/ManifestBuilder/ManifestBuildor.cs new file mode 100644 index 0000000..dc29af4 --- /dev/null +++ b/ManifestBuilder/ManifestBuildor.cs @@ -0,0 +1,86 @@ +/* + * Natural Launcher + +Copyright (C) 2018 Mael "Khelben" Vignaux + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact me with any question at the mail : mael.vignaux@elseware-experience.com +*/ +using Newtonsoft.Json; +using System.IO; +using System; +using System.Security.Cryptography; + +namespace ManifestBuilder +{ + class ManifestBuildor + { + public static string ManifestName = "Manifest.txt"; + + public static void BuildManifest(string directory) + { + + LauncherManifest manifest = new LauncherManifest(); + + md5 = MD5.Create(); + + RecursiveBuildManifest(directory, "", manifest); + + string ManifestPath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + ManifestName; + File.WriteAllText(ManifestPath, JsonConvert.SerializeObject(manifest, Formatting.Indented)); + } + + private static MD5 md5; + + private static void RecursiveBuildManifest(string projectRoot, string dir, LauncherManifest manifest) + { + string path = projectRoot + dir; + + foreach (string file in Directory.GetFiles(path)) + { + string localPath = ToLocalPath(projectRoot, file); + string hash = ComputeMD5(file); + + if(!localPath.EndsWith("_.cfg") && localPath != "/ManifestBuilder.exe" && localPath != "/Manifest.txt" + && localPath != "/Newtonsoft.Json.dll") //we don't want cfg files to get updated here cept config.cfg which is in ignore.list + manifest.Files[localPath] = hash; + } + + foreach (string nextDir in Directory.GetDirectories(path)) + { + RecursiveBuildManifest(projectRoot, ToLocalPath(projectRoot, nextDir), manifest); + } + } + + private static string ToLocalPath(string root, string dir) + { + return dir.Replace(root, "").Replace("\\", "/"); + } + + public static string ComputeMD5(string file) + { + MD5 md5 = MD5.Create(); + + string hash = ""; + using (var stream = File.OpenRead(file)) + { + hash = BitConverter.ToString(md5.ComputeHash(stream)).Replace("-", ""); + } + + return hash; + } + + } +} \ No newline at end of file diff --git a/ManifestBuilder/Program.cs b/ManifestBuilder/Program.cs new file mode 100644 index 0000000..058b543 --- /dev/null +++ b/ManifestBuilder/Program.cs @@ -0,0 +1,39 @@ +/* + * Natural Launcher + +Copyright (C) 2018 Mael "Khelben" Vignaux + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact me with any question at the mail : mael.vignaux@elseware-experience.com +*/ +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml; +using System.Xml.Linq; + +namespace ManifestBuilder +{ + class Program + { + static void Main(string[] args) + { + string curDir = Directory.GetCurrentDirectory(); + Console.WriteLine("Building manifest in : " + curDir); + ManifestBuildor.BuildManifest(curDir); + } + } +} diff --git a/ManifestBuilder/Properties/AssemblyInfo.cs b/ManifestBuilder/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..67611ed --- /dev/null +++ b/ManifestBuilder/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Les informations générales relatives à un assembly dépendent de +// l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations +// associées à un assembly. +[assembly: AssemblyTitle("ManifestBuilder")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ManifestBuilder")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly +// aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de +// COM, affectez la valeur true à l'attribut ComVisible sur ce type. +[assembly: ComVisible(false)] + +// Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM +[assembly: Guid("6a7dbe40-6bfb-49f5-bfc1-9f0752ec31be")] + +// Les informations de version pour un assembly se composent des quatre valeurs suivantes : +// +// Version principale +// Version secondaire +// Numéro de build +// Révision +// +// Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut +// en utilisant '*', comme indiqué ci-dessous : +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ManifestBuilder/packages.config b/ManifestBuilder/packages.config new file mode 100644 index 0000000..db03232 --- /dev/null +++ b/ManifestBuilder/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/NaturalLauncher.sln b/NaturalLauncher.sln new file mode 100644 index 0000000..a07341c --- /dev/null +++ b/NaturalLauncher.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2042 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NaturalLauncher", "NaturalLauncher\NaturalLauncher.csproj", "{0138DD06-7386-4820-9BCE-3F8412CAD801}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManifestBuilder", "ManifestBuilder\ManifestBuilder.csproj", "{6A7DBE40-6BFB-49F5-BFC1-9F0752EC31BE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanAndLaunch", "CleanAndLaunch\CleanAndLaunch.csproj", "{8E7856FD-FA68-4356-9C88-54C9EF5A9D20}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0138DD06-7386-4820-9BCE-3F8412CAD801}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0138DD06-7386-4820-9BCE-3F8412CAD801}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0138DD06-7386-4820-9BCE-3F8412CAD801}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0138DD06-7386-4820-9BCE-3F8412CAD801}.Release|Any CPU.Build.0 = Release|Any CPU + {6A7DBE40-6BFB-49F5-BFC1-9F0752EC31BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6A7DBE40-6BFB-49F5-BFC1-9F0752EC31BE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6A7DBE40-6BFB-49F5-BFC1-9F0752EC31BE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6A7DBE40-6BFB-49F5-BFC1-9F0752EC31BE}.Release|Any CPU.Build.0 = Release|Any CPU + {8E7856FD-FA68-4356-9C88-54C9EF5A9D20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E7856FD-FA68-4356-9C88-54C9EF5A9D20}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E7856FD-FA68-4356-9C88-54C9EF5A9D20}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E7856FD-FA68-4356-9C88-54C9EF5A9D20}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {06E5B626-B631-47BF-84D5-A157442007E0} + EndGlobalSection +EndGlobal diff --git a/NaturalLauncher/App.config b/NaturalLauncher/App.config new file mode 100644 index 0000000..5562283 --- /dev/null +++ b/NaturalLauncher/App.config @@ -0,0 +1,44 @@ + + + + +
+ + + + + + + + + http://ensl.gk-servers.de/ + + + http://ensl.gk-servers.de/ + + + http://ensl.gk-servers.de/ + + + http://ensl.gk-servers.de/ns + + + https://www.ensl.org/gathers/latest/ns1 + + + http://www.elseware-experience.com/vignauxmael/NaturalLauncher/NLPack + + + http://ensl.gk-servers.de/Launcher/ + + + + + + + + + + + + \ No newline at end of file diff --git a/NaturalLauncher/App.xaml b/NaturalLauncher/App.xaml new file mode 100644 index 0000000..2e4491c --- /dev/null +++ b/NaturalLauncher/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/NaturalLauncher/App.xaml.cs b/NaturalLauncher/App.xaml.cs new file mode 100644 index 0000000..3f76b19 --- /dev/null +++ b/NaturalLauncher/App.xaml.cs @@ -0,0 +1,12 @@ + +using System.Windows; + +namespace NaturalLauncher +{ + /// + /// Logique d'interaction pour App.xaml + /// + public partial class App : Application + { + } +} diff --git a/NaturalLauncher/ConsoleManager.cs b/NaturalLauncher/ConsoleManager.cs new file mode 100644 index 0000000..40c025e --- /dev/null +++ b/NaturalLauncher/ConsoleManager.cs @@ -0,0 +1,123 @@ +/* + * Natural Launcher + +Copyright (C) 2018 Mael "Khelben" Vignaux + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact me with any question at the mail : mael.vignaux@elseware-experience.com +*/ + +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Security; + + +namespace NaturalLauncher +{ + [SuppressUnmanagedCodeSecurity] + public static class ConsoleManager + { + private const string Kernel32_DllName = "kernel32.dll"; + + [DllImport(Kernel32_DllName)] + private static extern bool AllocConsole(); + + [DllImport(Kernel32_DllName)] + private static extern bool FreeConsole(); + + [DllImport(Kernel32_DllName)] + private static extern IntPtr GetConsoleWindow(); + + [DllImport(Kernel32_DllName)] + private static extern int GetConsoleOutputCP(); + + public static bool HasConsole + { + get { return GetConsoleWindow() != IntPtr.Zero; } + } + + /// + /// Creates a new console instance if the process is not attached to a console already. + /// + public static void Show() + { + //#if DEBUG + if (!HasConsole) + { + AllocConsole(); + InvalidateOutAndError(); + } + //#endif + } + + /// + /// If the process has a console attached to it, it will be detached and no longer visible. Writing to the System.Console is still possible, but no output will be shown. + /// + public static void Hide() + { + //#if DEBUG + if (HasConsole) + { + SetOutAndErrorNull(); + FreeConsole(); + } + //#endif + } + + public static void Toggle() + { + if (HasConsole) + { + Hide(); + } + else + { + Show(); + } + } + + static void InvalidateOutAndError() + { + Type type = typeof(System.Console); + + System.Reflection.FieldInfo _out = type.GetField("_out", + System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); + + System.Reflection.FieldInfo _error = type.GetField("_error", + System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); + + System.Reflection.MethodInfo _InitializeStdOutError = type.GetMethod("InitializeStdOutError", + System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); + + Debug.Assert(_out != null); + Debug.Assert(_error != null); + + Debug.Assert(_InitializeStdOutError != null); + + _out.SetValue(null, null); + _error.SetValue(null, null); + + _InitializeStdOutError.Invoke(null, new object[] { true }); + } + + static void SetOutAndErrorNull() + { + Console.SetOut(TextWriter.Null); + Console.SetError(TextWriter.Null); + } + } +} diff --git a/NaturalLauncher/Launcher.cs b/NaturalLauncher/Launcher.cs new file mode 100644 index 0000000..3d6d71f --- /dev/null +++ b/NaturalLauncher/Launcher.cs @@ -0,0 +1,275 @@ +/* + * Natural Launcher + +Copyright (C) 2018 Mael "Khelben" Vignaux + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact me with any question at the mail : mael.vignaux@elseware-experience.com +*/ +using System; +using System.IO; +using System.Diagnostics; +using System.Net; +using System.Windows; +using DiscordRPC; +using Newtonsoft.Json; +using System.Windows.Forms; + +namespace NaturalLauncher +{ + class Launcher + { + public static string curDir = Directory.GetCurrentDirectory(); + public static Uri MainPageURL = new Uri(Properties.Settings.Default.IndexURL); + public static Uri CreditPageURL = new Uri(Properties.Settings.Default.CreditURL); + + public static string HLFolder = ""; + public static string NSFolder; + + public static string VersionFileName = "app.version"; + public static string ManifestName = "Manifest.txt"; + public static string NLManifestName = "NLManifest.txt"; + public static string configName = "Launcher.xml"; + + public static bool keepLauncherAlive = true; + public static DateTime LAUNCHTIME = System.DateTime.UtcNow; + public static string discordCustomStatus = "Currently In The Launcher"; + + public static void PlayGame() + { + LAUNCHTIME = System.DateTime.UtcNow; //new time is launch time + UpdateDiscord(true); //doesnt work cause the timer set it back to false. need static var TODO + Process.Start("steam://rungameid/17558255459196993606"); + //Environment.Exit(0); // not stopping the process to let discord rpc live + if (!keepLauncherAlive) + Environment.Exit(0); + } + + internal static void UpdateDiscord(bool InGame) + { + int gatherPlayers = Util.ReadGathererCount(); + UpdatePubServ(out int pubPlayers, out int maxPlayers); + + int displayPlayers = pubPlayers; //by default + string discordState = "Players in public"; + + if (gatherPlayers > 6) + { + displayPlayers = gatherPlayers; + discordState = "Gather forming"; + } + + if (!InGame) + { + MainWindow.Discordclient.SetPresence(new RichPresence() + { + Details = discordCustomStatus, + State = discordState, + /*Secrets = new Secrets() + { + JoinSecret = "MTI4NzM0OjFpMmhuZToxMjMxMjM", + },*/ + Party = new Party() + { + ID = "ae488379 - 351d - 4a4f - ad32 - 2b9b01c91657", + Size = displayPlayers, + Max = 12, + }, + Timestamps = new Timestamps() + { + Start = LAUNCHTIME, + }, + Assets = new Assets() + { + LargeImageKey = "portrait_png", + LargeImageText = "Natural Selection Enhanced Launcher", + SmallImageKey = "skulku_png", + }, + }); + } + if (InGame) + { + MainWindow.Discordclient.SetPresence(new RichPresence() + { + Details = "In Game", + State = "Next gather", + Party = new Party() + { + ID = "ae488379 - 351d - 4a4f - ad32 - 2b9b01c91657", + Size = Util.ReadGathererCount(), + Max = 12, + }, + Timestamps = new Timestamps() + { + Start = LAUNCHTIME, + }, + Assets = new Assets() + { + LargeImageKey = "portrait_png", + LargeImageText = "Natural Selection Enhanced Launcher", + SmallImageKey = "skulku_png", + }, + }); + } + } + + public static void LaunchWebsite(string Url) + { + Process.Start(Url); + } + + public static void RefreshInternetPageAsync(string whichPage) + { + using (var webClient = new WebClient()) + { + if(whichPage == "Main") + { + string FileName = "index.html"; + webClient.DownloadFileAsync(MainPageURL, FileName); + } + + if (whichPage == "Credit") + { + string CreditFileName = "credit.html"; + webClient.DownloadFileAsync(CreditPageURL, CreditFileName); + } + } + } + + public static void CheckInstallDirectory() + { + bool NeedDirectory = false; + + if (File.Exists(curDir + Path.DirectorySeparatorChar + configName)) + { + string IndicatedFolder = ""; + bool IsNLPack = false; + XmlBuilder.ReadConfigXml(out IndicatedFolder, out IsNLPack, out string discordStatus, out bool keepAlive); + + if (IndicatedFolder.Length >0) + { + HLFolder = IndicatedFolder; + discordCustomStatus = discordStatus; + keepLauncherAlive = keepAlive; + } + else + NeedDirectory = true; + + if (!Directory.Exists(IndicatedFolder)) + NeedDirectory = true; + } + else + NeedDirectory = true; + + if (NeedDirectory) + { + string folderPath = Util.AskForHLFolder(); + + if (folderPath!=null) + { + HLFolder = folderPath; + NSFolder = HLFolder + Path.DirectorySeparatorChar + "ns"; + XmlBuilder.CreateConfigXml(); + + if (Directory.Exists(NSFolder)) + { + if(System.Windows.Forms.MessageBox.Show("Warning, you are updating an existing NS installation: " + + Environment.NewLine + "A backup of your old ns folder can be made under the name ns_saved.zip" + + Environment.NewLine + "Please choose if you want to backup your current ns folder.", "", MessageBoxButtons.YesNo, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1)== DialogResult.Yes) + { Util.ZipDirectory(NSFolder); } + } + else + { + Directory.CreateDirectory(NSFolder); + } + + } + else + { + Environment.Exit(0); //stop the process + } + } + + NSFolder = HLFolder + Path.DirectorySeparatorChar + "ns"; + + } + + public static bool IsNLPack() + { + bool Result = false; //by default + if (File.Exists(curDir + Path.DirectorySeparatorChar + configName)) + { + string IndicatedFolder = ""; + XmlBuilder.ReadConfigXml(out IndicatedFolder, out Result, out string plop, out bool noob); + } + else + { + Util.PlaySoundFX("error"); + MessageBoxResult AlertBox = System.Windows.MessageBox.Show("Launcher Config file not found"); + + CheckInstallDirectory(); + return false; + } + + return Result; + } + + public static void SetIsNLPack(bool IsNlPack) + { + XmlBuilder.CreateConfigXml(); + } + + public static void AddToIgnoreList() + { + Util.GetIgnoreManifest(true); //let's verify we have a ignore list from the start + + string IgnorePath = curDir + Path.DirectorySeparatorChar + "customignore.list"; + string IgnoreString = File.ReadAllText(IgnorePath); + LauncherManifest IgnoreManifest = JsonConvert.DeserializeObject(IgnoreString);// then get it + + var dialog = new System.Windows.Forms.OpenFileDialog(); + + dialog.InitialDirectory = NSFolder; + dialog.Multiselect = true; + + dialog.Title = "Please indicate what file(s) to add in the ignore list !"; + + if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + foreach(string file in dialog.FileNames) + { + string localPath = Util.ToLocalPath(NSFolder, file); + IgnoreManifest.Files[localPath] = "1"; //we using the launchermanifest but set "1" to ignore verify and update ("0" is ignore only update) + } + + File.WriteAllText(IgnorePath, JsonConvert.SerializeObject(IgnoreManifest, Formatting.Indented)); + } + } + + internal static void UpdatePubServ(out int publicPlayers, out int maxPlayers) + { + // TO BE CONTINUED but so far we can retrieve pub serv info... + string serverIP = "104.156.251.121"; //public us + int port = 27015; + IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(serverIP), port); + + var ServInfo = new ServerChecker.A2S_INFO(remoteEP); + + publicPlayers = ServInfo.Players; //out + maxPlayers = ServInfo.MaxPlayers; //out + // Check for the launcher to self update + } + } +} diff --git a/NaturalLauncher/LauncherManifest.cs b/NaturalLauncher/LauncherManifest.cs new file mode 100644 index 0000000..82b699e --- /dev/null +++ b/NaturalLauncher/LauncherManifest.cs @@ -0,0 +1,40 @@ +/* + * Natural Launcher + +Copyright (C) 2018 Mael "Khelben" Vignaux + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact me with any question at the mail : mael.vignaux@elseware-experience.com +*/ +using System.Collections.Generic; + +namespace NaturalLauncher +{ + class LauncherManifest + { + public LauncherManifest() + { + Files = new Dictionary(); + } + + public Dictionary Files + { + get; + set; + } + + } +} + diff --git a/NaturalLauncher/MainWindow.xaml b/NaturalLauncher/MainWindow.xaml new file mode 100644 index 0000000..03f1548 --- /dev/null +++ b/NaturalLauncher/MainWindow.xaml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + Ressources/nsl_icon.ico + + + + + + + + + + + + + + + diff --git a/NaturalLauncher/MainWindow.xaml.cs b/NaturalLauncher/MainWindow.xaml.cs new file mode 100644 index 0000000..bd0fe0f --- /dev/null +++ b/NaturalLauncher/MainWindow.xaml.cs @@ -0,0 +1,661 @@ +/* + * Natural Launcher + +Copyright (C) 2018 Mael "Khelben" Vignaux + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact me with any question at the mail : mael.vignaux@elseware-experience.com +*/ + +using System; +using System.ComponentModel; +using System.Windows; +using System.Security.Cryptography; +using System.IO; +using System.Net; +using System.Collections.Generic; +using Newtonsoft.Json; +using System.Threading; +using DiscordRPC; +using System.Timers; +using System.Diagnostics; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Resources; + +namespace NaturalLauncher +{ + /// + /// Logique d'interaction pour MainWindow.xaml + /// + public partial class MainWindow : Window + { + // TODO We need to know if ns files are being updated to lock the verify/update functions if so (with a file on the main serv). + + public static bool IsDebugMode = false; //no use anymore, but stay here just in case I need him + public static bool IsVerification = true; //true by default since it's more commun to verify than to update + + public static Settings sw; + + public static string versionNumber; + public static string remoteVersionNumber; + + public static bool downloadCancelled = false; + + public BackgroundWorker backgroundWorker; + public static DiscordRpcClient Discordclient; + + Uri LocalIndexURL = new Uri(String.Format("file:///{0}/index.html", Launcher.curDir)); + + private static string LogReportPath = Launcher.curDir; + TextWriter UpdateLog; + + public void MainWindow_Closed(object sender, EventArgs e) + { + if (sw != null) + { + sw.Close(); + } + } + + public MainWindow() + { + InitializeComponent(); + +#if DEBUG + IsDebugMode = true; +#endif + + SelfUpdater.DeleteOldFiles(); // not really needed since CleanAndLaunch.exe + + SelfUpdater TheSelfUpdater = new SelfUpdater(); + TheSelfUpdater.SetMainWindowRef(this); + TheSelfUpdater.UpdateLauncher(); + + + if (SelfUpdater.LaucherRemoteVNumber != SelfUpdater.LaucherLocalVNumber && SelfUpdater.isSelfUpdating) + { + this.Hide(); + } + else + { + // Set the title of the window with the correct launcher version + if (SelfUpdater.isSelfUpdating) + Launcher_Window.Title = "Natural Launcher v " + SelfUpdater.LaucherLocalVNumber; + else + Launcher_Window.Title = "Natural Launcher"; + + // CHECK THE HL AND NS INSTALL DIR AND WRITE IT IN THE LAUNCHER XML + Launcher.CheckInstallDirectory(); + + //Util.ChangeAValueInCfg("net_graph", "2"); //this is not useful but might be someday, that's why I still let it live + + // WE SET DISCORD PRESENCE + string ApplicationID = "474144509048127503"; + Discordclient = new DiscordRpcClient(ApplicationID); + + Discordclient.Initialize(); + + OnlineStatusAtLaunch(); + + System.Timers.Timer PresenceTimer = new System.Timers.Timer(); + PresenceTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent); + PresenceTimer.Enabled = true; + PresenceTimer.Interval = 5000; + + // *** Get the main index.html if connected *** \\ + if (Util.CheckForInternetConnection(Launcher.MainPageURL.AbsoluteUri)) //if we are connected + { + Launcher.RefreshInternetPageAsync("Main"); // we refresh the page + MainWebBrowser.Source = Launcher.MainPageURL; // and display it from source + } + else // if not simply show the saved file + { + MainWebBrowser.Navigate(LocalIndexURL.AbsolutePath); + } + + // CHECK APP.VERSION NUMBER + Check_Version(); + + if (versionNumber != remoteVersionNumber && versionNumber != "0.0.0.0") + { + MessageBoxResult AlertBox = System.Windows.MessageBox.Show("Update Needed !", "Update", MessageBoxButton.OK, MessageBoxImage.Exclamation); + StartButton.Content = "Please Update"; + IsVerification = false; + } + if (versionNumber == "0.0.0.0") + { + StartButton.Content = "Verify"; + IsVerification = false; + + } + if (versionNumber == remoteVersionNumber && versionNumber != "0.0.0.0") + { + StartButton.Content = "Play"; // TODO The algorithm to verify btween local and remote manifest + } + } + } + + + private void ReadyLauncherForUpdate(string FirstLogLine) + { + StartButton.IsEnabled = false; + VerifyButton.IsEnabled = false; + SettingButton.IsEnabled = false; + UpdateLog = new StreamWriter(LogReportPath + Path.DirectorySeparatorChar + "UpdateLog_" + Util.GetLongTimeString() + ".txt", true); // dt forget to close() + UpdateLog.WriteLine(Util.GetShortTimeString() + FirstLogLine); + } + + private void OnTimedEvent(object source, ElapsedEventArgs e) + { + Launcher.UpdateDiscord(false); + + Delegate myDelegate = (Action)Update_gatherButton; + Delegate secondDelegate = (Action)Update_ServLabel; + WebsiteButton.Dispatcher.Invoke(myDelegate); + ServInfoLabel.Dispatcher.Invoke(secondDelegate); + } + + private void OnlineStatusAtLaunch() + { + Launcher.UpdateDiscord(false); + + Delegate myDelegate = (Action)Update_gatherButton; + Delegate secondDelegate = (Action)Update_ServLabel; + WebsiteButton.Dispatcher.Invoke(myDelegate); + ServInfoLabel.Dispatcher.Invoke(secondDelegate); + } + + private void Update_gatherButton() + { + if(Util.ReadGathererCount()>0) + WebsiteButton.Content = "Join Gather ( " + Util.ReadGathererCount() + " / 12 )"; + else + WebsiteButton.Content = "Join Gather"; + } + + private void Update_ServLabel() + { + Launcher.UpdatePubServ(out int pubPlayers, out int maxPlayers); + ServInfoLabel.Content = "Public Server : " + pubPlayers + " / " + maxPlayers; + + } + + private void Check_Version() + { + // *** Get the version number *** \\ + versionNumber = Util.LocalVersion(Launcher.curDir + Path.DirectorySeparatorChar + Launcher.VersionFileName); + remoteVersionNumber = Util.RemoteVersion(Properties.Settings.Default.IndexURL + Launcher.VersionFileName); + + if (versionNumber != null) + { + VersionLabel.Content = versionNumber; + } + else //if it doesn exist, we need to inform the user + { + versionNumber = "0.0.0.0"; + VersionLabel.Content = "Verify"; + StartButton.Content = "Verify"; + MessageBoxResult AlertBox = System.Windows.MessageBox.Show("Please verify the installation" + Environment.NewLine + + "This message will show up if this is the first time you launch this software" + , "Alert", MessageBoxButton.OK, MessageBoxImage.Exclamation); //not true anymore + SettingButton.IsEnabled = false; + IsVerification = false; + } + } + + #region clicks + private void Start_Click(object sender, RoutedEventArgs e) + { + if (SelfUpdater.CheckOneFileSignature("NaturalLauncher.exe") || !SelfUpdater.isSelfUpdating) + { + if (versionNumber != remoteVersionNumber || versionNumber == "0.0.0.0") + { + ReadyLauncherForUpdate("Start Update from play button"); + StartButton.Content = "Verifying..."; + IsVerification = false; + UpdateGame(); //verify the game with the right click context menu + } + else + { + Launcher.PlayGame(); + Util.PlaySoundFX("finish"); + } + } + else + { + Util.PlaySoundFX("error"); + MessageBoxResult AlertBox = System.Windows.MessageBox.Show("Your launcher is corrupted or out of date, please relaunch" + Environment.NewLine + + "if the problem persist, please redownload the launcher from an official source (ensl.org)"); + File.Delete(Launcher.curDir + Path.DirectorySeparatorChar + "launcher.version"); + + Environment.Exit(0); + } + } + + private void Website_Click(object sender, RoutedEventArgs e) + { + Launcher.LaunchWebsite(Properties.Settings.Default.GatherURL); + } + + private void Setting_Click(object sender, RoutedEventArgs e) + { + if (sw == null) + { + sw = new Settings(); + } + sw.SetMainWindowRef(this); + sw.Show(); + } + + private void Verify_Click(object sender, RoutedEventArgs e) + { + ReadyLauncherForUpdate("Verification started from launcher"); + + if (versionNumber != remoteVersionNumber || versionNumber == "0.0.0.0") + { + IsVerification = false; + UpdateLog.WriteLine(Util.GetShortTimeString() + "but it's an update"); + } + else + { + IsVerification = true; + } + + UpdateGame(); //verify the game with the right click context menu + } + + private void Pause_Click(object sender, RoutedEventArgs e) + { + var brush = new ImageBrush(); + try + { + if (!downloadCancelled) + { + downloadCancelled = true; + backgroundWorker.CancelAsync(); + + Uri resourceUri = new Uri("Ressources/playimage.png", UriKind.Relative); //"pack://application:,,,/NaturalLauncher;component/Images/Resource_Image.png" + StreamResourceInfo streamInfo = Application.GetResourceStream(resourceUri); + + BitmapFrame temp = BitmapFrame.Create(streamInfo.Stream); + brush.ImageSource = temp; + + PauseButton.Background = brush; + + } + else + { + ReadyLauncherForUpdate("Verification resumed from launcher"); + UpdateGame(); //verify the game with the right click context menu + + Uri resourceUri = new Uri("Ressources/pauseimage.png", UriKind.Relative); + StreamResourceInfo streamInfo = Application.GetResourceStream(resourceUri); + + BitmapFrame temp = BitmapFrame.Create(streamInfo.Stream); + brush.ImageSource = temp; + + PauseButton.Background = brush; + } + } + catch + { + downloadCancelled = false; + Uri resourceUri = new Uri("Ressources/pauseimage.png", UriKind.Relative); + StreamResourceInfo streamInfo = Application.GetResourceStream(resourceUri); + + BitmapFrame temp = BitmapFrame.Create(streamInfo.Stream); + brush.ImageSource = temp; + + PauseButton.Background = brush; + } //I don't give a f if it doesnt catch shit + + } + #endregion + + #region ContextMenu + private void BuildManifest(object sender, RoutedEventArgs e) + { + ManifestBuilder.BuildManifest(Launcher.NSFolder); // build the manifest of the file to update + } + private void VerifyTheGame(object sender, RoutedEventArgs e) + { + /*UpdateLog = new StreamWriter(LogReportPath + Path.DirectorySeparatorChar + "UpdateLog_" + Util.GetLongTimeString() + ".txt", true); // dt forget to close() + UpdateLog.WriteLine(Util.GetShortTimeString() + "Verification started from context menu"); + UpdateGame(); //verify the game with the right click context menu*/ //not used anymore + } + private void CheckLauncherUpdate(object sender, RoutedEventArgs e) + { + SelfUpdater TheSelfUpdater = new SelfUpdater(); + TheSelfUpdater.allowMessage = true; + TheSelfUpdater.UpdateLauncher(); + } + private void JoinDiscord(object sender, RoutedEventArgs e) + { + Process.Start("https://discord.gg/ZUSSBUA"); + } + private void SeeCredit(object sender, RoutedEventArgs e) + { + Process.Start("https://discord.gg/ZUSSBUA"); + } + private void JoinPublicServer(object sender, RoutedEventArgs e) + { + Process.Start("steam://connect/104.156.251.121:27015"); + } + private void AddToIgnore(object sender, RoutedEventArgs e) + { + Launcher.AddToIgnoreList(); + } + + private void OpenNSFolder(object sender, RoutedEventArgs e) + { + Process.Start(Launcher.NSFolder); + } + #endregion + + #region UpdateMaelAlgo + + public void UpdateGame() + { + Util.PlaySoundFX("start"); + TaskbarItemInfo.ProgressState = System.Windows.Shell.TaskbarItemProgressState.Normal; + downloadCancelled = false; + backgroundWorker = new System.ComponentModel.BackgroundWorker(); + backgroundWorker.WorkerReportsProgress = true; + backgroundWorker.WorkerSupportsCancellation = true; + backgroundWorker.DoWork += UpdateGame_Worker; + backgroundWorker.ProgressChanged += UpdateGame_ProgressChanged; + backgroundWorker.RunWorkerCompleted += UpdateGame_RunWorkerCompleted; + backgroundWorker.RunWorkerAsync(); + } + + + public void CallUpdateGame() //same as updategame for now + { + ReadyLauncherForUpdate("update Called from a setting's change"); + UpdateGame(); + } + + + public void UpdateGame_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + if (e.Cancelled) + { + StartButton.Content = "Error"; + UpdateProgressBar.Value = 0; + MessageBoxResult AlertBox = System.Windows.MessageBox.Show("Error while downloading, please try again"); + ProgressLabel.Content = "Error while downloading, please try again"; + TaskbarItemInfo.ProgressState = System.Windows.Shell.TaskbarItemProgressState.Error; + return; + } + + if (downloadCancelled) + { + ProgressLabel.Content = "Paused !"; + UpdateLog.WriteLine(Util.GetShortTimeString() + "Stop requested by the user..."); + TaskbarItemInfo.ProgressState = System.Windows.Shell.TaskbarItemProgressState.Paused; //yellow thing + } + else + { + ProgressLabel.Content = "Up to date !"; + StartButton.Content = "Play"; + UpdateLog.WriteLine(Util.GetShortTimeString() + "Completed the update or verify work"); + TaskbarItemInfo.ProgressValue = 0; + TaskbarItemInfo.ProgressState = System.Windows.Shell.TaskbarItemProgressState.Normal; + Util.CreateLocalVersionFile(Launcher.curDir, Launcher.VersionFileName, remoteVersionNumber); //update the version with the new one + + Check_Version(); // we should have a changed app.version file to check + } + + UpdateLog.Close(); + backgroundWorker.Dispose(); + + Util.PlaySoundFX("finish"); + + StartButton.IsEnabled = true; + SettingButton.IsEnabled = true; + VerifyButton.IsEnabled = true; + } + + public void UpdateGame_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + ProgressLabel.Content = e.UserState as string; + UpdateProgressBar.Value = e.ProgressPercentage; + TaskbarItemInfo.ProgressValue = (double)((float)e.ProgressPercentage / 100f); + } + + public void UpdateGame_Worker(object sender, DoWorkEventArgs e) + { + backgroundWorker.ReportProgress(0, "Downloading Manifest..."); + + string ManifestURL = Launcher.MainPageURL.AbsoluteUri + Launcher.ManifestName; + + WebClient webClient = new WebClient(); + webClient.Headers.Add("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)"); + webClient.DownloadProgressChanged += webClient_DownloadProgressChanged; + webClient.DownloadFileCompleted += webClient_DownloadFileCompleted; + + string manifest = webClient.DownloadString(ManifestURL); + LauncherManifest RemoteManifest = JsonConvert.DeserializeObject(manifest); + + if (Launcher.IsNLPack()) + { + string NLManifestURL = Launcher.MainPageURL.AbsoluteUri + Launcher.NLManifestName; + string NLmanifest = webClient.DownloadString(NLManifestURL); + LauncherManifest NLManifest = JsonConvert.DeserializeObject(NLmanifest); + + RemoteManifest = Util.CleanManifestWithOptions(RemoteManifest, NLManifest); //cleaning the manifest hash consequently + } + + LauncherManifest IgnoreManifest = Util.GetIgnoreManifest(IsVerification); //attention, might not exist, if so it has to be downloaded, if not let's use it ! + + string gameInstallDir = Launcher.NSFolder; + UpdateLog.WriteLine(Util.GetShortTimeString() + "Install Directory located in : " + gameInstallDir); + UpdateLog.WriteLine(Util.GetShortTimeString() + "NL Pack installed : " + Launcher.IsNLPack().ToString()); + UpdateLog.WriteLine(Util.GetShortTimeString() + "Distant Manifest located at : " + ManifestURL); + + var md5 = MD5.Create(); + int totalFiles = RemoteManifest.Files.Count; + UpdateLog.WriteLine(Util.GetShortTimeString() + "Total file to update : " + totalFiles); + + int curFile = 0; + + // Update the standard files + foreach (KeyValuePair kv in RemoteManifest.Files) + { + bool ShouldDownload = false; + string gameFilePath = gameInstallDir + kv.Key.Replace("/", Path.DirectorySeparatorChar.ToString()); + IgnoreManifest.Files.TryGetValue(kv.Key, out string IgnoreValue); //get the ignore file value in manifest ("0" ignore only verif, "1" ignore verif + update) + + bool Condition1 = File.Exists(gameFilePath) && IgnoreManifest.Files.ContainsKey(kv.Key) == false;//if the file exists and not in the ignore manifest, let's have it ! + bool Condition2 = File.Exists(gameFilePath) && IgnoreManifest.Files.ContainsKey(kv.Key) && IgnoreValue == "0" && !IsVerification; //if the file exists, is in the ignore manifest, but is updatable and... it's update time ! + + if (Condition1 || Condition2) + { + int progress = (int)(((float)curFile / (float)totalFiles) * 100); + backgroundWorker.ReportProgress(progress, "(" + (curFile) + " / " + totalFiles + ") Checking " + kv.Key); + + //Check its md5 hash + using (var stream = File.OpenRead(gameFilePath)) + { + var hash = Util.ComputeMD5(gameFilePath); + + if (hash != kv.Value) + { + UpdateLog.WriteLine(Util.GetShortTimeString() + gameFilePath + " needs to be updated"); + ShouldDownload = true; + } + } + } + if (!File.Exists(gameFilePath)) + { + UpdateLog.WriteLine(Util.GetShortTimeString() + gameFilePath + " not existing, needs downloading"); + ShouldDownload = true; + } + if (File.Exists(gameFilePath) && IgnoreManifest.Files.ContainsKey(kv.Key) && IgnoreValue == "1") // ignore everything + { + UpdateLog.WriteLine(Util.GetShortTimeString() + gameFilePath + " Is Ignored for update, not checking"); + ShouldDownload = false; + } + if (File.Exists(gameFilePath) && IgnoreManifest.Files.ContainsKey(kv.Key) && IsVerification && IgnoreValue == "0") //if we need to ignore update and it's verification, we ignore ! + { + UpdateLog.WriteLine(Util.GetShortTimeString() + gameFilePath + " Is Ignored for verifications, not checking"); + ShouldDownload = false; + } + + + if (ShouldDownload) + { + DownloadFile(curFile, totalFiles, kv, RemoteManifest, gameFilePath, Properties.Settings.Default.GameUrl, webClient); + + var hash = Util.ComputeMD5(gameFilePath); + + if (hash != kv.Value) + { + //MessageBox.Show("Failed Validating " + kv.Key + " : Redownloading"); //problem with double validation, is it too soon in the chain of event ? + UpdateLog.WriteLine(Util.GetShortTimeString() + "failed to verify newly downloaded file, redownloading"); + DownloadFile(curFile, totalFiles, kv, RemoteManifest, gameFilePath, Properties.Settings.Default.GameUrl, webClient); + } + UpdateLog.WriteLine(Util.GetShortTimeString() + "Download complete for file: " + kv.Key + " with new hash :" + hash + " for manifest hash : " + kv.Value); + } + if (backgroundWorker.CancellationPending) + { + UpdateLog.WriteLine(Util.GetShortTimeString() + "Update cancelled"); + return; + } + + curFile++; + } + + // Update the NineLegend pack files if requiered + /*if (Launcher.IsNLPack()) + { + string NLManifestURL = Launcher.MainPageURL.AbsoluteUri + Launcher.NLManifestName; + string NLmanifest = webClient.DownloadString(NLManifestURL); + LauncherManifest NLManifest = JsonConvert.DeserializeObject(NLmanifest); + curFile = curFile - NLManifest.Files.Count; + + foreach (KeyValuePair kv in NLManifest.Files) + { + bool ShouldDownload = false; + string gameFilePath = gameInstallDir + kv.Key.Replace("/", Path.DirectorySeparatorChar.ToString()); + if (File.Exists(gameFilePath)) + { + int progress = (int)(((float)curFile / (float)totalFiles) * 100); + backgroundWorker.ReportProgress(progress, "(" + (curFile) + " / " + totalFiles + ") Checking " + kv.Key); + //Check its md5 hash + using (var stream = File.OpenRead(gameFilePath)) + { + var hash = Util.ComputeMD5(gameFilePath); + + if (hash != kv.Value) + { + UpdateLog.WriteLine(Util.GetShortTimeString() + gameFilePath + " needs to be updated"); + ShouldDownload = true; + } + } + } + else + { + UpdateLog.WriteLine(Util.GetShortTimeString() + gameFilePath + " not existing, needs downloading"); + ShouldDownload = true; + } + + if (ShouldDownload) + { + DownloadFile(curFile, totalFiles, kv, NLManifest, gameFilePath, Properties.Settings.Default.NineLegendUrl, webClient); + + var hash = Util.ComputeMD5(gameFilePath); + + if (hash != kv.Value) + { + //MessageBox.Show("Failed Validating " + kv.Key + " : Redownloading"); //problem with double validation, is it too soon in the chain of event ? + UpdateLog.WriteLine(Util.GetShortTimeString() + "failed to verify newly downloaded file, redownloading"); + DownloadFile(curFile, totalFiles, kv, NLManifest, gameFilePath, Properties.Settings.Default.NineLegendUrl, webClient); + } + UpdateLog.WriteLine(Util.GetShortTimeString() + "Download complete for NineLegend file: " + kv.Key + " with new hash :" + hash + " for manifest hash : " + kv.Value); + } + if (backgroundWorker.CancellationPending) + { + UpdateLog.WriteLine(Util.GetShortTimeString() + "Update cancelled"); + return; + } + + curFile++; + } + }*/ + + backgroundWorker.ReportProgress(100, "Writing Local Manifest"); + + /* string manifestDirectory = Launcher.curDir + Path.DirectorySeparatorChar + Launcher.ManifestName; + + File.WriteAllText(manifestDirectory, manifest); // replace all with function in util */ + } + + private void DownloadFile(int curFile, int totalFiles, KeyValuePair kv, LauncherManifest RemoteManifest, string gameFilePath, string folderURL, WebClient webClient) + { + int progress = (int)(((float)curFile / (float)totalFiles) * 100); + + string status = "(" + (curFile) + " / " + totalFiles + ") Downloading: " + kv.Key; + + UpdateLog.WriteLine(Util.GetShortTimeString() + "Downloading file: " + kv.Key); + + backgroundWorker.ReportProgress(progress, status); + + string remoteFile = (folderURL + "/" + kv.Key.Substring(1)); + + Directory.CreateDirectory(Path.GetDirectoryName(gameFilePath)); + if (File.Exists(gameFilePath)) + { + UpdateLog.WriteLine(Util.GetShortTimeString() + "File exist, deleting old file"); + File.Delete(gameFilePath); + } + + try + { + webClient.DownloadFileAsync(new Uri(remoteFile), gameFilePath, status); + } + catch (Exception e) + { + MessageBoxResult AlertBox = System.Windows.MessageBox.Show("new file not found"); + UpdateLog.WriteLine(Util.GetShortTimeString() + "Failed Downloading file: " + kv.Key + "with message : " + e.Message); + backgroundWorker.CancelAsync(); + } + + while (webClient.IsBusy) + { + Thread.Sleep(1); + } + } + + void webClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) + { + string status = e.UserState as string + " ( " + Util.Size(e.BytesReceived) + " / " + Util.Size(e.TotalBytesToReceive) + " )"; + backgroundWorker.ReportProgress(e.ProgressPercentage, status); + } + + void webClient_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) + { + if (e.Error != null) + { + System.Windows.MessageBox.Show(e.Error.ToString()); + backgroundWorker.CancelAsync(); + } + + } + #endregion + + } +} diff --git a/NaturalLauncher/ManifestBuilder.cs b/NaturalLauncher/ManifestBuilder.cs new file mode 100644 index 0000000..c216615 --- /dev/null +++ b/NaturalLauncher/ManifestBuilder.cs @@ -0,0 +1,74 @@ +/* + * Natural Launcher + +Copyright (C) 2018 Mael "Khelben" Vignaux + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact me with any question at the mail : mael.vignaux@elseware-experience.com +*/ +using Newtonsoft.Json; +using System.IO; +using System.Security.Cryptography; + +namespace NaturalLauncher +{ + class ManifestBuilder + { + + private static MD5 md5; + + public static void BuildManifest(string directory) + { + LauncherManifest manifest = new LauncherManifest(); + + md5 = MD5.Create(); + + RecursiveBuildManifest(directory, "", manifest); + + /*SaveFileDialog dialog = new SaveFileDialog(); // commented but needed when you want to save a manifest somewhere else (could be usefull for a manifest builder console app + dialog.InitialDirectory = Environment.CurrentDirectory; + dialog.FileName = "Manifest.txt"; + if (dialog.ShowDialog() == DialogResult.OK) + { + File.WriteAllText(dialog.FileName, JsonConvert.SerializeObject(manifest, Formatting.Indented)); + }*/ + + string ManifestPath = Launcher.curDir + Path.DirectorySeparatorChar + Launcher.ManifestName; + File.WriteAllText(ManifestPath, JsonConvert.SerializeObject(manifest, Formatting.Indented)); + } + + private static void RecursiveBuildManifest(string projectRoot, string dir, LauncherManifest manifest) + { + string path = projectRoot + dir; + + foreach (string file in Directory.GetFiles(path)) + { + string localPath = Util.ToLocalPath(projectRoot, file); + string hash = Util.ComputeMD5(file); + + if (!localPath.EndsWith("_.cfg") && localPath != "/Manifest.txt" && localPath != "/ManifestBuilder.exe" + && localPath != "/Newtonsoft.Json.dll") //we don't want cfg files to get updated here cept config.cfg which is in ignore.list + manifest.Files[localPath] = hash; + } + + foreach (string nextDir in Directory.GetDirectories(path)) + { + RecursiveBuildManifest(projectRoot, Util.ToLocalPath(projectRoot, nextDir), manifest); + } + } + + + } +} \ No newline at end of file diff --git a/NaturalLauncher/NaturalLauncher.csproj b/NaturalLauncher/NaturalLauncher.csproj new file mode 100644 index 0000000..9de3a1f --- /dev/null +++ b/NaturalLauncher/NaturalLauncher.csproj @@ -0,0 +1,211 @@ + + + + + Debug + AnyCPU + {0138DD06-7386-4820-9BCE-3F8412CAD801} + WinExe + NaturalLauncher + NaturalLauncher + v4.6.2 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + true + false + publish\ + true + Disk + true + Foreground + 7 + Days + false + true + true + en + Natural Launcher + Mael Vignaux + 1.0.0.0 + false + true + 0 + 1.0.0.%2a + false + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + Ressources\nsl_icon.ico + + + D8B30162E74CFDDB757978150D09F39672490FAC + + + NaturalLauncher_TemporaryKey.pfx + + + true + + + true + + + + False + ..\packages\DiscordRPC.dll + + + ..\packages\HtmlAgilityPack.1.8.5\lib\Net45\HtmlAgilityPack.dll + + + ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll + + + + + + + + + + + + + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + + + + + + + Settings.xaml + + + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + + MainWindow.xaml + Code + + + Designer + MSBuild:Compile + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + Designer + + + + + + + + + + + + + + + + + + + + False + Microsoft .NET Framework 4.6.2 %28x86 et x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NaturalLauncher/NaturalLauncher.sln b/NaturalLauncher/NaturalLauncher.sln new file mode 100644 index 0000000..a07341c --- /dev/null +++ b/NaturalLauncher/NaturalLauncher.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2042 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NaturalLauncher", "NaturalLauncher\NaturalLauncher.csproj", "{0138DD06-7386-4820-9BCE-3F8412CAD801}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManifestBuilder", "ManifestBuilder\ManifestBuilder.csproj", "{6A7DBE40-6BFB-49F5-BFC1-9F0752EC31BE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CleanAndLaunch", "CleanAndLaunch\CleanAndLaunch.csproj", "{8E7856FD-FA68-4356-9C88-54C9EF5A9D20}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0138DD06-7386-4820-9BCE-3F8412CAD801}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0138DD06-7386-4820-9BCE-3F8412CAD801}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0138DD06-7386-4820-9BCE-3F8412CAD801}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0138DD06-7386-4820-9BCE-3F8412CAD801}.Release|Any CPU.Build.0 = Release|Any CPU + {6A7DBE40-6BFB-49F5-BFC1-9F0752EC31BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6A7DBE40-6BFB-49F5-BFC1-9F0752EC31BE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6A7DBE40-6BFB-49F5-BFC1-9F0752EC31BE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6A7DBE40-6BFB-49F5-BFC1-9F0752EC31BE}.Release|Any CPU.Build.0 = Release|Any CPU + {8E7856FD-FA68-4356-9C88-54C9EF5A9D20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E7856FD-FA68-4356-9C88-54C9EF5A9D20}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E7856FD-FA68-4356-9C88-54C9EF5A9D20}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E7856FD-FA68-4356-9C88-54C9EF5A9D20}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {06E5B626-B631-47BF-84D5-A157442007E0} + EndGlobalSection +EndGlobal diff --git a/NaturalLauncher/ProjectSettings.cs b/NaturalLauncher/ProjectSettings.cs new file mode 100644 index 0000000..0c9678b --- /dev/null +++ b/NaturalLauncher/ProjectSettings.cs @@ -0,0 +1,30 @@ +/* + * Natural Launcher + +Copyright (C) 2018 Mael "Khelben" Vignaux + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact me with any question at the mail : mael.vignaux@elseware-experience.com +*/ + +namespace ArtefactsLauncher +{ + public class ProjectSettings + { + // Attention, it is very important to keep the exact same names as in the json + public float ScreenPercentage; + public float BeeFactor; + } +} diff --git a/NaturalLauncher/Properties/AssemblyInfo.cs b/NaturalLauncher/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..fc3433b --- /dev/null +++ b/NaturalLauncher/Properties/AssemblyInfo.cs @@ -0,0 +1,53 @@ +using System.Reflection; +using System.Runtime.InteropServices; +using System.Windows; + +// Les informations générales relatives à un assembly dépendent de +// l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations +// associées à un assembly. +[assembly: AssemblyTitle("NaturalLauncher")] +[assembly: AssemblyDescription("The launcher and updater for natural selection 1")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Mael Vignaux")] +[assembly: AssemblyProduct("NaturalLauncher")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly +// aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de +// COM, affectez la valeur true à l'attribut ComVisible sur ce type. +[assembly: ComVisible(false)] + +//Pour commencer à générer des applications localisables, définissez +//CultureUtiliséePourCoder dans votre fichier .csproj +//dans . Par exemple, si vous utilisez le français +//dans vos fichiers sources, définissez à fr-FR. Puis, supprimez les marques de commentaire de +//l'attribut NeutralResourceLanguage ci-dessous. Mettez à jour "fr-FR" dans +//la ligne ci-après pour qu'elle corresponde au paramètre UICulture du fichier projet. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //où se trouvent les dictionnaires de ressources spécifiques à un thème + //(utilisé si une ressource est introuvable dans la page, + // ou dictionnaires de ressources de l'application) + ResourceDictionaryLocation.SourceAssembly //où se trouve le dictionnaire de ressources générique + //(utilisé si une ressource est introuvable dans la page, + // dans l'application ou dans l'un des dictionnaires de ressources spécifiques à un thème) +)] + + +// Les informations de version pour un assembly se composent des quatre valeurs suivantes : +// +// Version principale +// Version secondaire +// Numéro de build +// Révision +// +// Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut +// en utilisant '*', comme indiqué ci-dessous : +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/NaturalLauncher/Properties/Resources.Designer.cs b/NaturalLauncher/Properties/Resources.Designer.cs new file mode 100644 index 0000000..d78cc63 --- /dev/null +++ b/NaturalLauncher/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// Ce code a été généré par un outil. +// Version du runtime :4.0.30319.42000 +// +// Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si +// le code est régénéré. +// +//------------------------------------------------------------------------------ + +namespace NaturalLauncher.Properties { + using System; + + + /// + /// Une classe de ressource fortement typée destinée, entre autres, à la consultation des chaînes localisées. + /// + // Cette classe a été générée automatiquement par la classe StronglyTypedResourceBuilder + // à l'aide d'un outil, tel que ResGen ou Visual Studio. + // Pour ajouter ou supprimer un membre, modifiez votre fichier .ResX, puis réexécutez ResGen + // avec l'option /str ou régénérez votre projet VS. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Retourne l'instance ResourceManager mise en cache utilisée par cette classe. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NaturalLauncher.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Remplace la propriété CurrentUICulture du thread actuel pour toutes + /// les recherches de ressources à l'aide de cette classe de ressource fortement typée. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/NaturalLauncher/Properties/Resources.resx b/NaturalLauncher/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/NaturalLauncher/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/NaturalLauncher/Properties/Settings.Designer.cs b/NaturalLauncher/Properties/Settings.Designer.cs new file mode 100644 index 0000000..39ebd6d --- /dev/null +++ b/NaturalLauncher/Properties/Settings.Designer.cs @@ -0,0 +1,110 @@ +//------------------------------------------------------------------------------ +// +// Ce code a été généré par un outil. +// Version du runtime :4.0.30319.42000 +// +// Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si +// le code est régénéré. +// +//------------------------------------------------------------------------------ + +namespace NaturalLauncher.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://ensl.gk-servers.de/")] + public string ConfigurationUrl { + get { + return ((string)(this["ConfigurationUrl"])); + } + set { + this["ConfigurationUrl"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://ensl.gk-servers.de/")] + public string IndexURL { + get { + return ((string)(this["IndexURL"])); + } + set { + this["IndexURL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://ensl.gk-servers.de/")] + public string CreditURL { + get { + return ((string)(this["CreditURL"])); + } + set { + this["CreditURL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://ensl.gk-servers.de/ns")] + public string GameUrl { + get { + return ((string)(this["GameUrl"])); + } + set { + this["GameUrl"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("https://www.ensl.org/gathers/latest/ns1")] + public string GatherURL { + get { + return ((string)(this["GatherURL"])); + } + set { + this["GatherURL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://www.elseware-experience.com/vignauxmael/NaturalLauncher/NLPack")] + public string NineLegendUrl { + get { + return ((string)(this["NineLegendUrl"])); + } + set { + this["NineLegendUrl"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://ensl.gk-servers.de/Launcher/")] + public string LauncherUrl { + get { + return ((string)(this["LauncherUrl"])); + } + set { + this["LauncherUrl"] = value; + } + } + } +} diff --git a/NaturalLauncher/Properties/Settings.settings b/NaturalLauncher/Properties/Settings.settings new file mode 100644 index 0000000..257d880 --- /dev/null +++ b/NaturalLauncher/Properties/Settings.settings @@ -0,0 +1,27 @@ + + + + + + http://ensl.gk-servers.de/ + + + http://ensl.gk-servers.de/ + + + http://ensl.gk-servers.de/ + + + http://ensl.gk-servers.de/ns + + + https://www.ensl.org/gathers/latest/ns1 + + + http://www.elseware-experience.com/vignauxmael/NaturalLauncher/NLPack + + + http://ensl.gk-servers.de/Launcher/ + + + \ No newline at end of file diff --git a/NaturalLauncher/Ressources/Commande.lex b/NaturalLauncher/Ressources/Commande.lex new file mode 100644 index 0000000..9072573 --- /dev/null +++ b/NaturalLauncher/Ressources/Commande.lex @@ -0,0 +1,144 @@ +parameter +value +_snd_mixahead +ati_npatch +bgmvolume +bottomcolor +brightness +cl_allowdownload +cl_allowupload +cl_autohelp +cl_backspeed +cl_buildmessages +cl_centerentityid +cl_cmdbackup +cl_cmdrate +cl_cmhotkeys +cl_cross +cl_cross_alpha +cl_cross_bottom_line +cl_cross_color +cl_cross_dot_color +cl_cross_dot_outline +cl_cross_dot_size +cl_cross_gap +cl_cross_left_line +cl_cross_outline +cl_cross_outline_inner +cl_cross_right_line +cl_cross_size +cl_cross_thickness +cl_cross_top_line +cl_customcrosshair +cl_dlmax +cl_download_ingame +cl_dynamiclights +cl_forcedefaultfov +cl_forwardspeed +cl_gammaramp +cl_highdetail +cl_himodels +cl_hudmapzoom +cl_iconb +cl_icong +cl_iconr +cl_idealpitchscale +cl_labelhivesight +cl_labelmaps +cl_lc +cl_logocolor +cl_logofile +cl_lw +cl_mousegrab +cl_musicdelay +cl_musicdirectory +cl_musicenabled +cl_musicvolume +cl_particleinfo +cl_quickselecttime +cl_timeout +cl_updaterate +cl_vsmoothing +cl_widescreen +con_color +con_mono +console +crosshair +fps_max +gamma +gl_dither +gl_flipmatrix +gl_fog +gl_monolights +gl_overbright +gl_polyoffset +gl_vsync +hisound +hpk_maxsize +hud_capturemouse +hud_centerid +hud_classautokill +hud_draw +hud_fastswitch +hud_takesshots +joystick +lookspring +lookstrafe +m_customaccel +m_customaccel_exponent +m_customaccel_max +m_customaccel_scale +m_filter +m_forward +m_mousethread_sleep +m_pitch +m_rawinput +m_side +m_yaw +model +MP3FadeTime +MP3Volume +mp_decals +name +net_graph +net_scale +r_detailtextures +sensitivity +skin +suitvolume +sv_aim +sv_voiceenable +team +topcolor +viewsize +voice_enable +voice_forcemicrecord +voice_modenable +voice_scale +volume +sv_cheats +hud_style +cl_cmdrate +cl_updaterate +ex_extrapmax +cl_cmdbackup +rate +ex_interp +gl_wateramp +gl_zmax +gl_ztrick +r_drawviewmodel +r_mmx +r_dynamic +r_decals +r_mirroralpha +r_bmodelhighfrac +violence_ablood +violence_agibs +violence_hblood +violence_hgibs +cl_labelmaps +senslock +cl_ambientsound +cl_widescreen +sv_jumpmode \ No newline at end of file diff --git a/NaturalLauncher/Ressources/background.png b/NaturalLauncher/Ressources/background.png new file mode 100644 index 0000000..973702d Binary files /dev/null and b/NaturalLauncher/Ressources/background.png differ diff --git a/NaturalLauncher/Ressources/background_blur.png b/NaturalLauncher/Ressources/background_blur.png new file mode 100644 index 0000000..4ab8f96 Binary files /dev/null and b/NaturalLauncher/Ressources/background_blur.png differ diff --git a/NaturalLauncher/Ressources/nsl_icon.ico b/NaturalLauncher/Ressources/nsl_icon.ico new file mode 100644 index 0000000..aad2710 Binary files /dev/null and b/NaturalLauncher/Ressources/nsl_icon.ico differ diff --git a/NaturalLauncher/Ressources/pauseimage.png b/NaturalLauncher/Ressources/pauseimage.png new file mode 100644 index 0000000..c23568c Binary files /dev/null and b/NaturalLauncher/Ressources/pauseimage.png differ diff --git a/NaturalLauncher/Ressources/playimage.png b/NaturalLauncher/Ressources/playimage.png new file mode 100644 index 0000000..1b90bb4 Binary files /dev/null and b/NaturalLauncher/Ressources/playimage.png differ diff --git a/NaturalLauncher/Ressources/sdx_error.wav b/NaturalLauncher/Ressources/sdx_error.wav new file mode 100644 index 0000000..4f5e300 Binary files /dev/null and b/NaturalLauncher/Ressources/sdx_error.wav differ diff --git a/NaturalLauncher/Ressources/sdx_finished.wav b/NaturalLauncher/Ressources/sdx_finished.wav new file mode 100644 index 0000000..df28577 Binary files /dev/null and b/NaturalLauncher/Ressources/sdx_finished.wav differ diff --git a/NaturalLauncher/Ressources/sdx_started.wav b/NaturalLauncher/Ressources/sdx_started.wav new file mode 100644 index 0000000..7097fff Binary files /dev/null and b/NaturalLauncher/Ressources/sdx_started.wav differ diff --git a/NaturalLauncher/Ressources/small_discordicon.png b/NaturalLauncher/Ressources/small_discordicon.png new file mode 100644 index 0000000..e12021a Binary files /dev/null and b/NaturalLauncher/Ressources/small_discordicon.png differ diff --git a/NaturalLauncher/Ressources/steam_icon.png b/NaturalLauncher/Ressources/steam_icon.png new file mode 100644 index 0000000..c17dd94 Binary files /dev/null and b/NaturalLauncher/Ressources/steam_icon.png differ diff --git a/NaturalLauncher/SelfUpdater.cs b/NaturalLauncher/SelfUpdater.cs new file mode 100644 index 0000000..b30ed8f --- /dev/null +++ b/NaturalLauncher/SelfUpdater.cs @@ -0,0 +1,325 @@ +/* + * Natural Launcher + +Copyright (C) 2018 Mael "Khelben" Vignaux + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact me with any question at the mail : mael.vignaux@elseware-experience.com +*/ + +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Net; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; + +namespace NaturalLauncher +{ + class SelfUpdater + { + public static bool isSelfUpdating = true; + + public static string LaucherLocalVNumber; + public static string LaucherRemoteVNumber; + public static string curDir = Directory.GetCurrentDirectory(); + + public static MainWindow MainWindowReference; + + public static string LauncherFolderURL = Properties.Settings.Default.LauncherUrl; + public static string VersionFileName = "launcher.version"; + public static string PublicKeyPath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "NslKey.cer"; + public static string manifestUrl = Properties.Settings.Default.IndexURL + "CryptoManifest.txt"; + + public static LauncherManifest CryptoManifest = new LauncherManifest(); + + public static int NumberOfFiles = 0; + + public bool allowMessage=false; + + public BackgroundWorker backgroundWorker; + + public void SetMainWindowRef(MainWindow MainWindowRef) + { + MainWindowReference = MainWindowRef; + } + + public void UpdateLauncher() + { + if(isSelfUpdating) + { + CheckUpdaterVersion(); + + DeleteOldFiles(); //delete all the updatelog and .bak files + + if (LaucherLocalVNumber != LaucherRemoteVNumber) + { + UpdateCryptoManifest(); + + // Move the files in the launcher directory to a name with .bak + MoveLauncherFiles(); //Attention, non recursive !!! + + // falsify all the buttons + MainWindowReference.StartButton.IsEnabled = false; + MainWindowReference.VerifyButton.IsEnabled = false; + MainWindowReference.SettingButton.IsEnabled = false; + + // We gotta update the launcher ! + MessageBoxResult AlertBox = System.Windows.MessageBox.Show("The Launcher Will be self updating !" + Environment.NewLine + "It will restart itself once done..."); //not true anymore + ConsoleManager.Show(); + + // background worker download the files and switch the launcher when over + backgroundWorker = new System.ComponentModel.BackgroundWorker(); + backgroundWorker.WorkerSupportsCancellation = true; + backgroundWorker.DoWork += UpdateLauncher_Worker; + backgroundWorker.RunWorkerCompleted += UpdateLauncher_RunWorkerCompleted; + backgroundWorker.RunWorkerAsync(); + } + else + { + if (allowMessage) //inform the player we don't need update (only when using context menu so far) + { + MessageBoxResult AlertBox = System.Windows.MessageBox.Show("No Update Required !"); + } + } + } + + } + + private static void UpdateCryptoManifest() + { + using (WebClient webClient = new WebClient()) + { + Console.WriteLine("Downloading the CryptoManifest"); + string dasManifest = webClient.DownloadString(new Uri(manifestUrl)); + CryptoManifest = JsonConvert.DeserializeObject(dasManifest); + NumberOfFiles = CryptoManifest.Files.Count; + } + } + + public static void CheckUpdaterVersion() + { + LaucherLocalVNumber = Util.LocalVersion(curDir + Path.DirectorySeparatorChar + VersionFileName); + Console.WriteLine("Checking launcher's Version ..."); + Console.WriteLine("Local Version of the updater is :" + LaucherLocalVNumber); + if (LaucherLocalVNumber != null) + { + LaucherRemoteVNumber = Util.RemoteVersion(Launcher.MainPageURL + VersionFileName); + Console.WriteLine("Got the remote version with success :" + LaucherRemoteVNumber); + } + else //if it doesn exist, we need to inform the user + { + LaucherLocalVNumber = "0.0.0.0"; + Util.PlaySoundFX("error"); + LaucherRemoteVNumber = Util.RemoteVersion(Launcher.MainPageURL + VersionFileName); + Console.WriteLine("Couldnt find remote version number"); + } + } + + public static void MoveLauncherFiles() + { + foreach (KeyValuePair kv in CryptoManifest.Files) + { + string fullpath = curDir + Path.DirectorySeparatorChar + kv.Key.Remove(0,1); + if (File.Exists(fullpath)) + { + Console.WriteLine(fullpath + " : Changing to .bak extension ..."); + System.IO.File.Move(fullpath, fullpath + ".bak"); + } + } + } + + /*public static void AddNewFilesToChangeList() + { + // Check the new files + List NewFiles = Util.GetDirectoryFilesFromUrl(LauncherFolderURL); + + // we add the new files into the changedlist + foreach (string file in NewFiles) + { + if (!ChangedFiles.Contains(file)) + ChangedFiles.Add(file); + } + + NumberOfFiles = ChangedFiles.Count; + }*/ + + public void UpdateLauncher_Worker(object sender, DoWorkEventArgs e) + { + WebClient webClient = new WebClient(); + webClient.Headers.Add("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)"); + webClient.DownloadFileCompleted += webClient_DownloadFileCompleted; + + // Download the changed files + foreach (KeyValuePair kv in CryptoManifest.Files) + { + string smtg = kv.Key.Remove(0, 1); + DownloadLauncherFiles(kv.Key.Remove(0,1), webClient); + } + + } + + public void UpdateLauncher_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + if (!backgroundWorker.IsBusy && NumberOfFiles<=0) //double check but somehow it executed twice oO + { + CheckSignatureAndLaunch(); + } + } + + public void DownloadLauncherFiles(string file, WebClient webClient) + { + Console.WriteLine(file + " : Downloading ..."); + webClient.DownloadFileAsync(new Uri(LauncherFolderURL+ file), curDir + Path.DirectorySeparatorChar + file); + + while (webClient.IsBusy) + { + Thread.Sleep(1); + } + } + + void webClient_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) + { + if (e.Error != null) + { + System.Windows.MessageBox.Show(e.Error.ToString()); + } + else + { + try + { + NumberOfFiles--; + Console.WriteLine("Downloaded !" + NumberOfFiles.ToString() + " more files to go !"); + } + catch + { } + } + } + + public static void DeleteOldFiles() + { + Console.WriteLine("Cleaning old and update files ..."); + foreach (string file in Directory.GetFiles(curDir)) + { + // Cleaning BAK files + if(Path.GetExtension(file)==".bak") + { + File.Delete(file); + } + string localPath = Util.ToLocalPath(curDir, file); + + // Cleaning UpdateLogs files + if (localPath.StartsWith("/UpdateLog")) + { + File.Delete(file); + } + } + } + + public static void CheckSignatureAndLaunch() + { + Console.WriteLine("Starting signature check"); + foreach (KeyValuePair kv in CryptoManifest.Files) + { + string FullPath = curDir + Path.DirectorySeparatorChar + kv.Key.Remove(0, 1); + var stream = File.ReadAllBytes(FullPath); + string signatureHexaString = ""; + CryptoManifest.Files.TryGetValue(kv.Key, out signatureHexaString); + + if (!Verify(stream, PublicKeyPath, signatureHexaString)) + { + Util.PlaySoundFX("error"); + System.Windows.MessageBox.Show(kv.Key + " unvalidated. Your version is corrupted, please redownload the launcher from a trusted source (ensl.org)"); + Console.WriteLine(kv.Key + " : UNVALIDATED and DELETED..."); + File.Delete(FullPath); + Console.Read(); + Environment.Exit(0); + } + else + Console.WriteLine(kv.Key + " : validated ..."); + } + + Task.Delay(10); + Console.WriteLine("Refreshing version Number"); + Util.CreateLocalVersionFile(curDir, VersionFileName, LaucherRemoteVNumber); + + Util.LaunchAgain(curDir + Path.DirectorySeparatorChar + "CleanAndLaunch.exe"); + + Environment.Exit(0); + } + + public static bool Verify(byte[] data, string PublicKeyPath, string SignatureHexaString) + { + X509Certificate2 publicKey = LoadPublicKey(PublicKeyPath); + byte[] signature = StringToByte(SignatureHexaString); + + if (data == null) + { + throw new ArgumentNullException("data"); + } + + if (publicKey == null) + { + throw new ArgumentNullException("publicKey"); + } + + if (signature == null) + { + throw new ArgumentNullException("signature"); + } + + var provider = (RSACryptoServiceProvider)publicKey.PublicKey.Key; + + return provider.VerifyData(data, new SHA1CryptoServiceProvider(), signature); + + } + + public static X509Certificate2 LoadPublicKey(string PublicKeyPath) + { + return new X509Certificate2(PublicKeyPath); + } + + public static byte[] StringToByte(string str) + { + String[] arr = str.Split('-'); + byte[] array = new byte[arr.Length]; + for (int i = 0; i < arr.Length; i++) array[i] = Convert.ToByte(arr[i], 16); + return array; + } + + public static bool CheckOneFileSignature(string fileName) + { + UpdateCryptoManifest(); //first let's update the crypto manifest with latest values + + foreach (KeyValuePair kv in CryptoManifest.Files) + { + if (kv.Key.Remove(0, 1) == fileName) // we found the filename in the manifest + { + string FullPath = curDir + Path.DirectorySeparatorChar + kv.Key.Remove(0, 1); + var stream = File.ReadAllBytes(FullPath); + string signatureHexaString = ""; + CryptoManifest.Files.TryGetValue(kv.Key, out signatureHexaString); + return Verify(stream, PublicKeyPath, signatureHexaString); + } + } + return false; + } + } +} diff --git a/NaturalLauncher/ServerChecker.cs b/NaturalLauncher/ServerChecker.cs new file mode 100644 index 0000000..49f5532 --- /dev/null +++ b/NaturalLauncher/ServerChecker.cs @@ -0,0 +1,183 @@ +/* + * Natural Launcher + +Copyright (C) 2018 Mael "Khelben" Vignaux + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact me with any question at the mail : mael.vignaux@elseware-experience.com +*/ + +using System; +using System.Globalization; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Text; + +namespace NaturalLauncher +{ + class ServerChecker + { + public class A2S_INFO + { + // \xFF\xFF\xFF\xFFTSource Engine Query\x00 because UTF-8 doesn't like to encode 0xFF + public static readonly byte[] REQUEST = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0x54, 0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x45, 0x6E, 0x67, 0x69, 0x6E, 0x65, 0x20, 0x51, 0x75, 0x65, 0x72, 0x79, 0x00 }; + #region Strong Typing Enumerators + [Flags] + public enum ExtraDataFlags : byte + { + GameID = 0x01, + SteamID = 0x10, + Keywords = 0x20, + Spectator = 0x40, + Port = 0x80 + } + public enum VACFlags : byte + { + Unsecured = 0, + Secured = 1 + } + public enum VisibilityFlags : byte + { + Public = 0, + Private = 1 + } + public enum EnvironmentFlags : byte + { + Linux = 0x6C, //l + Windows = 0x77, //w + Mac = 0x6D, //m + MacOsX = 0x6F //o + } + public enum ServerTypeFlags : byte + { + Dedicated = 0x64, //d + Nondedicated = 0x6C, //l + SourceTV = 0x70 //p + } + #endregion + public byte Header { get; set; } // I + public byte Protocol { get; set; } + public string Name { get; set; } + public string Map { get; set; } + public string Folder { get; set; } + public string Game { get; set; } + public short ID { get; set; } + public byte Players { get; set; } + public byte MaxPlayers { get; set; } + public byte Bots { get; set; } + public ServerTypeFlags ServerType { get; set; } + public EnvironmentFlags Environment { get; set; } + public VisibilityFlags Visibility { get; set; } + public VACFlags VAC { get; set; } + public string Version { get; set; } + public ExtraDataFlags ExtraDataFlag { get; set; } + #region Extra Data Flag Members + public ulong GameID { get; set; } //0x01 + public ulong SteamID { get; set; } //0x10 + public string Keywords { get; set; } //0x20 + public string Spectator { get; set; } //0x40 + public short SpectatorPort { get; set; } //0x40 + public short Port { get; set; } //0x80 + #endregion + + public A2S_INFO(IPEndPoint ep) + { + UdpClient udp = new UdpClient(); + udp.Send(REQUEST, REQUEST.Length, ep); + MemoryStream ms = new MemoryStream(udp.Receive(ref ep)); // Saves the received data in a memory buffer + BinaryReader br = new BinaryReader(ms, Encoding.UTF8); // A binary reader that treats charaters as Unicode 8-bit + ms.Seek(4, SeekOrigin.Begin); // skip the 4 0xFFs + Header = br.ReadByte(); + Protocol = br.ReadByte(); + Name = ReadNullTerminatedString(ref br); + Map = ReadNullTerminatedString(ref br); + Folder = ReadNullTerminatedString(ref br); + Game = ReadNullTerminatedString(ref br); + ID = br.ReadInt16(); + Players = br.ReadByte(); + MaxPlayers = br.ReadByte(); + Bots = br.ReadByte(); + ServerType = (ServerTypeFlags)br.ReadByte(); + Environment = (EnvironmentFlags)br.ReadByte(); + Visibility = (VisibilityFlags)br.ReadByte(); + VAC = (VACFlags)br.ReadByte(); + Version = ReadNullTerminatedString(ref br); + ExtraDataFlag = (ExtraDataFlags)br.ReadByte(); + #region These EDF readers have to be in this order because that's the way they are reported + if (ExtraDataFlag.HasFlag(ExtraDataFlags.Port)) + Port = br.ReadInt16(); + if (ExtraDataFlag.HasFlag(ExtraDataFlags.SteamID)) + SteamID = br.ReadUInt64(); + if (ExtraDataFlag.HasFlag(ExtraDataFlags.Spectator)) + { + SpectatorPort = br.ReadInt16(); + Spectator = ReadNullTerminatedString(ref br); + } + if (ExtraDataFlag.HasFlag(ExtraDataFlags.Keywords)) + Keywords = ReadNullTerminatedString(ref br); + if (ExtraDataFlag.HasFlag(ExtraDataFlags.GameID)) + GameID = br.ReadUInt64(); + #endregion + br.Close(); + ms.Close(); + udp.Close(); + } + /// Reads a null-terminated string into a .NET Framework compatible string. + /// Binary reader to pull the null-terminated string from. Make sure it is correctly positioned in the stream before calling. + /// String of the same encoding as the input BinaryReader. + public static string ReadNullTerminatedString(ref BinaryReader input) + { + StringBuilder sb = new StringBuilder(); + char read = input.ReadChar(); + while (read != '\x00') + { + sb.Append(read); + read = input.ReadChar(); + } + return sb.ToString(); + } + + // Handles IPv4 and IPv6 notation. + public static IPEndPoint CreateIPEndPoint(string endPoint) + { + string[] ep = endPoint.Split(':'); + if (ep.Length < 2) throw new FormatException("Invalid endpoint format"); + IPAddress ip; + if (ep.Length > 2) + { + if (!IPAddress.TryParse(string.Join(":", ep, 0, ep.Length - 1), out ip)) + { + throw new FormatException("Invalid ip-adress"); + } + } + else + { + if (!IPAddress.TryParse(ep[0], out ip)) + { + throw new FormatException("Invalid ip-adress"); + } + } + int port; + if (!int.TryParse(ep[ep.Length - 1], NumberStyles.None, NumberFormatInfo.CurrentInfo, out port)) + { + throw new FormatException("Invalid port"); + } + return new IPEndPoint(ip, port); + } + + } + } +} diff --git a/NaturalLauncher/Settings.xaml b/NaturalLauncher/Settings.xaml new file mode 100644 index 0000000..308d0c5 --- /dev/null +++ b/NaturalLauncher/Settings.xaml @@ -0,0 +1,43 @@ + + + + + + + + +