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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/NaturalLauncher/Settings.xaml.cs b/NaturalLauncher/Settings.xaml.cs
new file mode 100644
index 0000000..76a02cd
--- /dev/null
+++ b/NaturalLauncher/Settings.xaml.cs
@@ -0,0 +1,259 @@
+/*
+ * 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.Net;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Forms;
+
+namespace NaturalLauncher
+{
+ ///
+ /// Logique d'interaction pour Settings.xaml
+ ///
+ public partial class Settings : Window
+ {
+
+ public static MainWindow MainWindowReference;
+ public static bool windowfullyopen = false;
+
+ public Settings()
+ {
+ InitializeComponent();
+
+ ParamNameTxtbox.SpellCheck.IsEnabled = true;
+ ParamNameTxtbox.SpellCheck.CustomDictionaries.Add(new Uri("pack://application:,,,/Ressources/Commande.lex")); //add the command custom dictonary
+
+ //RefreshNLInstallButtonState(); //nl pack based
+
+ Util.GetAValueInCfg("hud_style",out string HUDStyleparam);
+ switch(HUDStyleparam)
+ {
+ case "0":
+ ClassicRadioButton.IsChecked = true;
+ break;
+ case "1":
+ MinimalRadioButton.IsChecked = true;
+ break;
+ case "2":
+ NLRadioButton.IsChecked = true;
+ break;
+ }
+
+ XmlBuilder.ReadConfigXml(out string uno, out bool dos, out string discordStatus, out bool keepAlive);
+ Launcher.keepLauncherAlive = keepAlive;
+ DiscordTxtbox.Text = discordStatus;
+ KeepAliveChecker.IsChecked = keepAlive;
+
+ windowfullyopen = true;
+
+ }
+
+ public void SetMainWindowRef(MainWindow MainWindowRef)
+ {
+ MainWindowReference = MainWindowRef;
+ }
+
+ private void Window_Closed(object sender, EventArgs e)
+ {
+ MainWindow.sw = null;
+ windowfullyopen = false;
+ }
+
+ private void FindParameter_Click(object sender, RoutedEventArgs e)
+ {
+ try
+ {
+ Util.GetAValueInCfg(ParamNameTxtbox.Text,out string ParamValue);
+ ParamValueTxtbox.Text = ParamValue;
+ }
+ catch
+ {
+ ParamValueTxtbox.Text = "not found";
+ }
+ }
+
+ private void ChangeParameter_Click(object sender, RoutedEventArgs e)
+ {
+ try
+ {
+ if(ParamValueTxtbox.Text != "not found")
+ {
+ Util.ChangeAValueInCfg(ParamNameTxtbox.Text, ParamValueTxtbox.Text);
+ }
+ }
+ catch
+ {
+
+ }
+ }
+
+ private void RefreshNLInstallButtonState()
+ {
+ if (Launcher.IsNLPack())
+ {
+ NLInstallButton.IsEnabled = false;
+ NLUnInstallButton.IsEnabled = true;
+ }
+ else
+ {
+ NLInstallButton.IsEnabled = true;
+ NLUnInstallButton.IsEnabled = false;
+ }
+ }
+
+ private void NLInstallButton_Click(object sender, RoutedEventArgs e)
+ {
+ // TODO Install THE NL PACK, USING NL FOLDERS URL
+ Launcher.SetIsNLPack(true);
+ RefreshNLInstallButtonState();
+ MainWindowReference.CallUpdateGame();
+ MainWindowReference.SettingButton.IsEnabled = false;
+ this.Hide();
+ }
+
+ private void NLUnInstallButton_Click(object sender, RoutedEventArgs e)
+ {
+ // TODO REMOVE THE NL PACK FILES USING NL FOLDERS URL
+ // THEN REPAIR INSTALLATION
+ Launcher.SetIsNLPack(false);
+ RefreshNLInstallButtonState();
+ MainWindowReference.CallUpdateGame();
+ MainWindowReference.SettingButton.IsEnabled = false;
+ this.Hide();
+ }
+
+ private void AdvSettingsInstallButton_Click(object sender, RoutedEventArgs e)
+ {
+ // GET THE CONFIG FILES INSIDE THE GAME URL. SHOULDNT NEED A MANIFEST, JUST GET CFGs
+ try
+ {
+ if (System.Windows.Forms.MessageBox.Show("You are about to download a set of configuration files "
+ + Environment.NewLine + "These files will override your current and existing configuration files if they exists."
+ + Environment.NewLine + "Please chose if you want to continue.", "", MessageBoxButtons.YesNo, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1) == System.Windows.Forms.DialogResult.Yes)
+ {
+ using (WebClient webClient = new WebClient()) // yeah I know it's brutal :D
+ {
+ webClient.DownloadFile(new Uri(Properties.Settings.Default.GameUrl + "/config.cfg"), Launcher.NSFolder + System.IO.Path.DirectorySeparatorChar + "config.cfg");
+ webClient.DownloadFile(new Uri(Properties.Settings.Default.GameUrl + "/userconfig.cfg"), Launcher.NSFolder + System.IO.Path.DirectorySeparatorChar + "userconfig.cfg");
+ webClient.DownloadFile(new Uri(Properties.Settings.Default.GameUrl + "/alien_.cfg"), Launcher.NSFolder + System.IO.Path.DirectorySeparatorChar + "alien_.cfg");
+ webClient.DownloadFile(new Uri(Properties.Settings.Default.GameUrl + "/pistol_.cfg"), Launcher.NSFolder + System.IO.Path.DirectorySeparatorChar + "pistol_.cfg");
+ webClient.DownloadFile(new Uri(Properties.Settings.Default.GameUrl + "/reset_.cfg"), Launcher.NSFolder + System.IO.Path.DirectorySeparatorChar + "reset_.cfg");
+ webClient.DownloadFile(new Uri(Properties.Settings.Default.GameUrl + "/rine_.cfg"), Launcher.NSFolder + System.IO.Path.DirectorySeparatorChar + "rine_.cfg");
+ }
+
+ MessageBoxResult AlertBox = System.Windows.MessageBox.Show("Advanced settings Installed with success");
+ }
+ }
+ catch
+ {
+ MessageBoxResult AlertBox = System.Windows.MessageBox.Show("Advanced settings failed to install");
+ }
+ }
+
+ private void AdvSettingsUnInstallButton_Click(object sender, RoutedEventArgs e)
+ {
+ // REMOVE THE CFG FILES. ENDWITH(config.cfg) AND ENDWITH(_.cfg). GAME SHOULD RECREATE THE CONFIG FILE
+ try
+ {
+ foreach (string file in Directory.GetFiles(Launcher.NSFolder))
+ {
+ if (file.EndsWith("_.cfg") || file.EndsWith("config.cfg"))
+ File.Delete(file);
+ }
+ MessageBoxResult AlertBox = System.Windows.MessageBox.Show("Advanced settings Removed with success");
+ }
+ catch
+ {
+ MessageBoxResult AlertBox = System.Windows.MessageBox.Show("Failed to remove settings");
+ }
+ }
+
+ private void BrowseHLFolderButton_Click(object sender, RoutedEventArgs e)
+ {
+ // BROWSE AND CHANGE THE FOLDER FILE. DONT FORGET TO UPDATE THE GLOBAL VARIABLE
+ bool IsItNLPack = Launcher.IsNLPack();
+ string folderPath = Util.AskForHLFolder();
+
+ if (folderPath!=null)
+ {
+ XmlBuilder.CreateConfigXml();
+ Launcher.HLFolder = folderPath;
+ Launcher.NSFolder = folderPath + System.IO.Path.DirectorySeparatorChar + "ns";
+ }
+
+ MainWindowReference.CallUpdateGame();
+ }
+
+ private void ClassicRadioButton_Checked(object sender, RoutedEventArgs e)
+ {
+ if(windowfullyopen)
+ {
+ ClassicRadioButton.IsChecked = true;
+ MinimalRadioButton.IsChecked = false;
+ NLRadioButton.IsChecked = false;
+ Util.ChangeAValueInCfg("hud_style", "0", true);
+ }
+ }
+
+ private void MinimalRadioButton_Checked(object sender, RoutedEventArgs e)
+ {
+ if (windowfullyopen)
+ {
+ ClassicRadioButton.IsChecked = false;
+ MinimalRadioButton.IsChecked = true;
+ NLRadioButton.IsChecked = false;
+ Util.ChangeAValueInCfg("hud_style", "1", true);
+ }
+ }
+
+ private void NLRadioButton_Checked(object sender, RoutedEventArgs e)
+ {
+ if (windowfullyopen)
+ {
+ ClassicRadioButton.IsChecked = false;
+ MinimalRadioButton.IsChecked = false;
+ NLRadioButton.IsChecked = true;
+ Util.ChangeAValueInCfg("hud_style", "2", true);
+ }
+ }
+
+ private void AddToIgnoreButton_Click(object sender, RoutedEventArgs e)
+ {
+ Launcher.AddToIgnoreList();
+ }
+
+ private void StopAfterLaunch_Click(object sender, RoutedEventArgs e)
+ {
+ Launcher.keepLauncherAlive = KeepAliveChecker.IsChecked.Value;
+
+ XmlBuilder.CreateConfigXml();
+ }
+
+ private void ChangeDiscord_Click(object sender, RoutedEventArgs e)
+ {
+ Launcher.discordCustomStatus = DiscordTxtbox.Text;
+ Launcher.UpdateDiscord(false);
+ XmlBuilder.CreateConfigXml();
+ }
+ }
+}
diff --git a/NaturalLauncher/Util.cs b/NaturalLauncher/Util.cs
new file mode 100644
index 0000000..6c06b21
--- /dev/null
+++ b/NaturalLauncher/Util.cs
@@ -0,0 +1,665 @@
+/*
+ * 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 HtmlAgilityPack;
+using Microsoft.Win32;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Security.Cryptography;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Windows;
+using System.IO.Compression;
+using System.Windows.Resources;
+
+namespace NaturalLauncher
+{
+ class Util
+ {
+ 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;
+ }
+
+ public static string LocalVersion(string path)
+ {
+ string lv = null;
+
+ if (string.IsNullOrEmpty(path)
+ || System.IO.Path.GetInvalidPathChars().Intersect(
+ path.ToCharArray()).Count() != 0
+ || !new System.IO.FileInfo(path).Exists)
+ {
+ lv = null;
+ }
+ else if (new System.IO.FileInfo(path).Exists)
+ {
+ string s = System.IO.File.ReadAllText(path);
+ if (ValidateFile(s)) {
+ lv = s;
+ }
+ else {
+ lv = null;
+ }
+ }
+
+ return lv;
+ }
+
+ public static string CreateLocalVersionFile(string folderPath,
+ string fileName, string version)
+ {
+ if (!new System.IO.DirectoryInfo(folderPath).Exists)
+ {
+ System.IO.Directory.CreateDirectory(folderPath);
+ }
+
+ string path = folderPath + Path.DirectorySeparatorChar + fileName;
+
+ if (new System.IO.FileInfo(path).Exists)
+ {
+ new System.IO.FileInfo(path).Delete();
+ }
+
+ if (!new System.IO.FileInfo(path).Exists)
+ {
+ System.IO.File.WriteAllText(path, version);
+ }
+ return path;
+ }
+
+ // method used to check internet connectivity
+ public static bool CheckForInternetConnection(string UrlToCheck)
+ {
+ try
+ {
+ using (var client = new WebClient())
+ using (client.OpenRead(UrlToCheck)) // or http://clients3.google.com/generate_204
+ {
+ return true;
+ }
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ public static bool ValidateFile(string contents)
+ {
+ bool val = false;
+ if (!string.IsNullOrEmpty(contents))
+ {
+ string pattern = "^([0-9]*\\.){3}[0-9]*$";
+ System.Text.RegularExpressions.Regex re =
+ new System.Text.RegularExpressions.Regex(pattern);
+ val = re.IsMatch(contents);
+ }
+ return val;
+ }
+
+ public static string RemoteVersion(string url)
+ {
+ string rv = "";
+
+ try
+ {
+ System.Net.HttpWebRequest req = (System.Net.HttpWebRequest)
+ System.Net.WebRequest.Create(url);
+ System.Net.HttpWebResponse response =
+ (System.Net.HttpWebResponse)req.GetResponse();
+ System.IO.Stream receiveStream = response.GetResponseStream();
+ System.IO.StreamReader readStream =
+ new System.IO.StreamReader(receiveStream, Encoding.UTF8);
+ string s = readStream.ReadToEnd();
+ response.Close();
+ if (ValidateFile(s))
+ {
+ rv = s;
+ }
+ }
+ catch (Exception)
+ {
+ // Anything could have happened here but
+ // we don't want to stop the user
+ // from using the application.
+ rv = null;
+ }
+ return rv;
+ }
+
+ public static string Size(long bytes)
+ {
+ if (bytes > 1000000000)
+ {
+ return ((float)bytes / 1000000000f).ToString("f") + " GB";
+ }
+
+ if (bytes > 1000000)
+ {
+ return ((float)bytes / 1000000f).ToString("f") + " MB";
+ }
+ if (bytes > 1000)
+ {
+ return ((float)bytes / 1000f).ToString("f") + " KB";
+ }
+ return ((float)bytes).ToString("f") + " B";
+ }
+
+ public static string ToLocalPath(string root, string dir)
+ {
+ return dir.Replace(root, "").Replace("\\", "/");
+ }
+
+ public static string GetLongTimeString()
+ {
+ var src = DateTime.Now;
+ string StringToReturn = src.Month + "_" + src.Day + "_" + src.Hour + "_" + src.Minute + "_" + src.Second;
+ return StringToReturn;
+ }
+
+ public static string GetShortTimeString()
+ {
+ var src = DateTime.Now;
+ string StringToReturn = "[ " + src.Hour + " : " + src.Minute + " : " + src.Second + " ] : ";
+ return StringToReturn;
+ }
+
+ public static string GetSteamFolder(bool WantCommon) //just testing if ever needed
+ {
+ string path = "";
+ // HKEY_CURRENT_USER/Software/Valve/Steam
+ try
+ {
+ using (RegistryKey key = Registry.CurrentUser.OpenSubKey("Software\\Valve\\Steam"))
+ {
+ if (key != null)
+ {
+ Object o = key.GetValue("SteamPath");
+ if (o != null)
+ {
+ path = o as String;
+
+ if(WantCommon)
+ path = path + "/steamapps/common";
+
+ path = Path.GetFullPath(path);
+
+ Console.WriteLine(path);
+
+ // NEEDS A NUGGET PACKAGE GAMELOOP.VDF TO READ
+ // To get full list of game folders : steamapps\libraryfolders.vdf
+ /*string LibraryFoldersPath = path + Path.DirectorySeparatorChar + "steamapps" + Path.DirectorySeparatorChar + "libraryfolders.vdf";
+
+ // we need a function to extract folder directory
+ dynamic FolderLibrary = VdfConvert.Deserialize(File.ReadAllText(LibraryFoldersPath));
+ var FolderLibraryValues = FolderLibrary.Value;
+ var importantJsonObject = FolderLibrary.ToJson();*/
+ }
+ }
+ }
+ }
+ catch
+ {
+ MessageBoxResult AlertBox = MessageBox.Show("Steam Folder not found");
+ path = "";
+ }
+
+ return path;
+
+ }
+
+ public static string GetHLFolder() //just testing if ever needed
+ {
+ string path = "";
+ // HKEY_CURRENT_USER/Software/Valve/Steam
+ try
+ {
+ using (RegistryKey key = Registry.CurrentUser.OpenSubKey("Software\\Valve\\Steam"))
+ {
+ if (key != null)
+ {
+ Object o = key.GetValue("ModInstallPath");
+ if (o != null)
+ {
+ path = o as String;
+
+ path = Path.GetFullPath(path);
+
+ Console.WriteLine(path);
+ }
+ }
+ }
+ }
+ catch
+ {
+ MessageBoxResult AlertBox = MessageBox.Show("HL Folder not found","Alert", MessageBoxButton.OK , MessageBoxImage.Error);
+ path = "";
+ }
+
+ return path;
+
+ }
+
+ public static int ReadGathererCount()
+ {
+ var url = Properties.Settings.Default.GatherURL;
+ var web = new HtmlWeb();
+ var doc = web.Load(url);
+ string htmlstring = doc.Text;
+ int Count = 0;
+ // need to retrieve this :
+ var nodal = doc.DocumentNode.Descendants("ul");
+
+ for(int i = 0; i < nodal.Count(); i++)
+ {
+ try
+ {
+ if (nodal.ElementAt(i).Id == "gatherers")
+ {
+ Count = nodal.ElementAt(i).SelectNodes("li").Count(); //and count the inside
+ }
+ }
+ catch
+ {
+ Count = 0;
+ }
+
+ }
+
+ return Count;
+ }
+
+ // This function is here to compare the general "standard" ns install with the advanced "NlPack" install version
+ // If the user is using the NLPack then the manifest has to be updated consequently.
+ public static LauncherManifest CleanManifestWithOptions(LauncherManifest GeneralManifest, LauncherManifest NLManifest)
+ {
+ //string value = "";
+ foreach (KeyValuePair kv in NLManifest.Files)
+ {
+ if (GeneralManifest.Files.ContainsKey(kv.Key))
+ {
+ // we should delete, not to verify this. Then a second period of the updater check changed with the nl folder and its own manifest.
+ // the following code is in case you want to change the value (commented)
+
+ /*NLManifest.Files.TryGetValue(kv.Key, out value);
+ GeneralManifest.Files[kv.Key] = value; */
+
+ GeneralManifest.Files.Remove(kv.Key);
+ }
+ }
+
+ return GeneralManifest;
+ }
+
+ public static bool RemoteFileExists(string url)
+ {
+ try
+ {
+ //Creating the HttpWebRequest
+ HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
+ //Setting the Request method HEAD, you can also use GET too.
+ request.Method = "HEAD";
+ //Getting the Web Response.
+ HttpWebResponse response = request.GetResponse() as HttpWebResponse;
+ //Returns TRUE if the Status code == 200
+ response.Close();
+ return (response.StatusCode == HttpStatusCode.OK);
+ }
+ catch
+ {
+ //Any exception will returns false.
+ return false;
+ }
+ }
+
+ public static List TheDirectory = new List();
+
+ public static List GetDirectoryFilesFromUrl(string url)
+ {
+ HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
+ using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
+ {
+ using (StreamReader reader = new StreamReader(response.GetResponseStream()))
+ {
+ string html = reader.ReadToEnd();
+ Regex regex = new Regex(GetDirectoryListingRegexForUrl(url));
+ MatchCollection matches = regex.Matches(html);
+ if (matches.Count > 0)
+ {
+ foreach (Match match in matches)
+ {
+ if (match.Success)
+ {
+ TheDirectory.Add(match.Groups["name"].ToString());
+ }
+ }
+ }
+ }
+ }
+
+ TheDirectory.Remove("Parent Directory");
+
+ return TheDirectory;
+
+ }
+
+ public static string GetDirectoryListingRegexForUrl(string url)
+ {
+ if (url.Equals(url))
+ {
+ return "(?.*) "; //problem with the .exe.config file
+ }
+ throw new NotSupportedException();
+ }
+
+ public static string AskForHLFolder()
+ {
+ string folderPath = "";
+ var dialog = new System.Windows.Forms.OpenFileDialog();
+
+ dialog.InitialDirectory = Util.GetHLFolder();
+ dialog.Filter = "Half life Executable|hl.exe";
+
+ dialog.Title = "Please indicate where is your Half Life steam executable !";
+
+ if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
+ {
+ folderPath = dialog.FileName;
+ folderPath = folderPath.Remove(folderPath.Length + 1 - 8);
+ return folderPath;
+ }
+ else
+ {
+ Util.PlaySoundFX("error");
+ MessageBoxResult AlertBox = System.Windows.MessageBox.Show("This launcher need your half life directory in order to function. Please relaunch.", "Alert", MessageBoxButton.OK, MessageBoxImage.Error);
+
+ folderPath = null;
+ return folderPath;
+ }
+ }
+
+ public static LauncherManifest GetIgnoreManifest(bool isVerification)
+ {
+ // we first get the ignore.list used by dev
+ string IgnorePath = Launcher.curDir + Path.DirectorySeparatorChar + "ignore.list";
+ LauncherManifest NewManifest = new LauncherManifest();
+
+ if (File.Exists(IgnorePath) && isVerification) //then we can use the one in the folder. That's ok
+ {
+ string IgnoreString = File.ReadAllText(IgnorePath);
+ NewManifest = JsonConvert.DeserializeObject(IgnoreString);
+ }
+ else
+ {
+ try
+ {
+ using (WebClient webClient = new WebClient()) // yeah I know it's brutal :D
+ {
+ webClient.DownloadFile(new Uri(Properties.Settings.Default.IndexURL + "/ignore.list"), IgnorePath);
+ }
+
+ string IgnoreString = File.ReadAllText(IgnorePath);
+ NewManifest = JsonConvert.DeserializeObject(IgnoreString);
+ }
+ catch
+ {
+ /*MessageBoxResult AlertBox = System.Windows.MessageBox.Show("Could not retrieve a new ignore manifest file, Creating a void one...");
+ NewManifest.Files["/config.cfg"] = "";*/
+ Util.PlaySoundFX("error");
+ MessageBoxResult AlertBox = System.Windows.MessageBox.Show("Launcher couldn't find a correct ignore.list from online source, please verify you internet connection..."
+ , "Alert", MessageBoxButton.OK, MessageBoxImage.Error);
+
+ throw new Exception("Launcher couldn't find a correct ignore.list from online source, please verify you internet connection...");
+ }
+ }
+ // then we read the custom one on the disk
+ string CustomIgnorePath = Launcher.curDir + Path.DirectorySeparatorChar + "customignore.list";
+ LauncherManifest CustomignoreManifest = new LauncherManifest();
+
+ if (File.Exists(CustomIgnorePath))
+ {
+ string IgnoreString = File.ReadAllText(CustomIgnorePath);
+ CustomignoreManifest = JsonConvert.DeserializeObject(IgnoreString);
+ }
+ else
+ {
+ CustomignoreManifest.Files["/exemple.xpl"] = "1";
+
+ File.WriteAllText(CustomIgnorePath, JsonConvert.SerializeObject(CustomignoreManifest, Formatting.Indented)); //write the new voidy ignore manifest
+ }
+
+ // then we add both manifest to return the ignore list
+ foreach(KeyValuePair kv in CustomignoreManifest.Files)
+ {
+ NewManifest.Files.Add(kv.Key, kv.Value);
+ }
+
+ return NewManifest;
+ }
+
+ public static void CopyDirectory(string SourcePath, string DestinationPath) //not used anymore.
+ {
+ //Now Create all of the directories
+ foreach (string dirPath in Directory.GetDirectories(SourcePath, "*",
+ SearchOption.AllDirectories))
+ Directory.CreateDirectory(dirPath.Replace(SourcePath, DestinationPath));
+
+ ConsoleManager.Show();
+ //Copy all the files & Replaces any files with the same name
+ foreach (string newPath in Directory.GetFiles(SourcePath, "*.*",
+ SearchOption.AllDirectories))
+ {
+ /*string newdirr = Path.GetDirectoryName(newPath);
+ if (!newdirr.Contains("sound") || !newPath.Contains("maps") || !newPath.Contains("dlls") || !newPath.Contains("cl_dlls"))
+ {*/
+ Console.WriteLine(Path.GetFileName(newPath) + " being copied...");
+ File.Copy(newPath, newPath.Replace(SourcePath, DestinationPath), true);
+ //}
+ }
+
+ ConsoleManager.Hide();
+
+ }
+
+ public static void ZipDirectory(string SourceDirPath)
+ {
+ ConsoleManager.Show();
+ Console.WriteLine("Ziping directory...");
+ Console.WriteLine("This can take up to 2 minutes, please wait...");
+ ZipFile.CreateFromDirectory(SourceDirPath, SourceDirPath + "_saved.zip", CompressionLevel.Optimal,false);
+ Console.WriteLine("Ziping done !");
+ ConsoleManager.Hide();
+ }
+
+ public static bool GetAValueInCfg(string ValueName, out string Value)
+ {
+ string[] CfgLines = File.ReadAllLines(Launcher.NSFolder + Path.DirectorySeparatorChar + "config.cfg");
+ string[] UCfgLines = File.ReadAllLines(Launcher.NSFolder + Path.DirectorySeparatorChar + "userconfig.cfg");
+ bool found = false;
+ Value = "not found";
+
+ // first we look in the config.cfg then we will look in userconfig and keep its value if it exists (as it's the last cfg executed)
+ foreach (string Line in CfgLines)
+ {
+ if(Line.StartsWith(ValueName))
+ {
+ Value = Line.Remove(0, ValueName.Length + 2); //on enleve tout le debut et le premier guillemet
+ Value = Value.Remove(Value.Length - 1, 1); // et le dernier
+ found = true;
+ //initialInt = Convert.ToInt32(initialValue);
+ }
+ }
+
+ // if it's the userconfig, take that instead
+ foreach (string Line in UCfgLines)
+ {
+ if (Line.StartsWith(ValueName))
+ {
+ Value = Line.Remove(0, ValueName.Length + 2); //on enleve tout le debut et le premier guillemet
+ Value = Value.Remove(Value.Length - 1, 1); // et le dernier
+ found = true;
+ //initialInt = Convert.ToInt32(initialValue);
+ }
+ }
+
+ return found;
+ }
+
+ public static void ChangeAValueInCfg(string ValueName,string Value,bool isStrictToConfig = false)
+ {
+ string[] CfgLines = File.ReadAllLines(Launcher.NSFolder + Path.DirectorySeparatorChar + "config.cfg");
+ string[] UCfgLines = File.ReadAllLines(Launcher.NSFolder + Path.DirectorySeparatorChar + "userconfig.cfg");
+ bool cfgchange = false;
+ bool ucfgchange = false;
+ bool found = false;
+ int loopindex = 0;
+ // first we look in the config.cfg then we will look in userconfig and keep its value if it exists (as it's the last cfg executed)
+ foreach (string Line in CfgLines)
+ {
+ if (Line.StartsWith(ValueName))
+ {
+ CfgLines[loopindex] = ValueName + " " + "\"" + Value + "\"";
+ found = true;
+ cfgchange = true;
+ }
+ loopindex++;
+ }
+
+ if (isStrictToConfig) //if should only be in config.cfg, let's verify it's not in usercfg
+ {
+ loopindex = 0;
+ foreach (string Line in UCfgLines)
+ {
+ if (Line.StartsWith(ValueName))
+ {
+ UCfgLines[loopindex] = ""; //we remove the doublon
+ ucfgchange = true;
+ }
+ loopindex++;
+ }
+ }
+ if (!isStrictToConfig) //we can check usercfg
+ {
+ loopindex = 0;
+ foreach (string Line in UCfgLines)
+ {
+ if (Line.StartsWith(ValueName))
+ {
+ UCfgLines[loopindex] = ValueName + " " + "\"" + Value + "\"";
+ found = true;
+ ucfgchange = true;
+ }
+ loopindex++;
+ }
+ }
+ if (!found && !isStrictToConfig) //we didnt find it, let's add it to usercfg!
+ {
+ Array.Resize(ref UCfgLines, UCfgLines.Length + 1);
+ UCfgLines[UCfgLines.Length - 1] = ValueName + " " + "\"" + Value + "\"";
+ ucfgchange = true;
+ }
+
+ if (!found && isStrictToConfig) //we didnt find it, let's add it to config cfg !
+ {
+ Array.Resize(ref CfgLines, CfgLines.Length + 1);
+ CfgLines[CfgLines.Length - 1] = ValueName + " " + "\"" + Value + "\"";
+ cfgchange = true;
+ }
+
+ // ecrire le deux fichiers à l'emplacement source !
+ try
+ {
+ if(cfgchange)
+ {
+ File.WriteAllLines(Launcher.NSFolder + Path.DirectorySeparatorChar + "config.cfg", CfgLines);
+ }
+ }
+ catch
+ {
+ System.Windows.MessageBox.Show("Could not write config.cfg, please verify the file is not read only !", "Read only", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ try
+ {
+ if (ucfgchange)
+ {
+ File.WriteAllLines(Launcher.NSFolder + Path.DirectorySeparatorChar + "userconfig.cfg", UCfgLines);
+ }
+ }
+ catch
+ {
+ System.Windows.MessageBox.Show("Could not write userconfig.cfg, please verify the file is not read only !" + Environment.NewLine +
+ "This problem may also be caused by a hud_style setting in your userconfig.cfg file !", "Read only", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+
+ }
+
+ public static void LaunchAgain(string PathToExe)
+ {
+ ProcessStartInfo processInfo = new ProcessStartInfo();
+ processInfo.WorkingDirectory = Path.GetDirectoryName(PathToExe);
+ processInfo.FileName = Path.GetFileName(PathToExe);
+ processInfo.ErrorDialog = true;
+ processInfo.UseShellExecute = false;
+ processInfo.RedirectStandardOutput = true;
+ processInfo.RedirectStandardError = true;
+ Process proc = Process.Start(processInfo);
+ }
+
+ public static void PlaySoundFX(string Type) //now only play when error because whiners
+ {
+ Uri resourceUri = new Uri("Ressources/sdx_error.wav", UriKind.Relative);
+ switch (Type)
+ {
+ case "error":
+ resourceUri = new Uri("Ressources/sdx_error.wav", UriKind.Relative);
+ StreamResourceInfo streamInfo = Application.GetResourceStream(resourceUri);
+
+ System.Media.SoundPlayer player = new System.Media.SoundPlayer(streamInfo.Stream);
+ player.Play();
+ break;
+ case "start":
+ resourceUri = new Uri("Ressources/sdx_started.wav", UriKind.Relative);
+ break;
+ case "finish":
+ resourceUri = new Uri("Ressources/sdx_finished.wav", UriKind.Relative);
+ break;
+ default:
+ resourceUri = new Uri("Ressources/sdx_error.wav", UriKind.Relative);
+ break;
+ }
+
+
+ }
+ }
+}
diff --git a/NaturalLauncher/XmlBuilder.cs b/NaturalLauncher/XmlBuilder.cs
new file mode 100644
index 0000000..7fd2503
--- /dev/null
+++ b/NaturalLauncher/XmlBuilder.cs
@@ -0,0 +1,131 @@
+/*
+ * 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.IO;
+using System.Linq;
+using System.Xml;
+using System.Xml.Linq;
+
+namespace NaturalLauncher
+{
+ class XmlBuilder
+ {
+
+ public static bool BuildXmlDocument(string ManifestRootPath)
+ {
+ DirectoryInfo dir = new DirectoryInfo(ManifestRootPath);
+ var doc = new XDocument(new XDeclaration("1.0", "UTF-8", "yes"),
+ StartCreateXML(dir));
+
+ try
+ {
+ File.WriteAllText(ManifestRootPath + Path.DirectorySeparatorChar + "Manifest.xml", doc.ToString()); //write the new voidy ignore manifest
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+
+ }
+
+ public static XElement StartCreateXML(DirectoryInfo dir) // create a full directory xml
+ {
+ var xmlInfo = new XElement("MainDirectory");
+ //get all the files first
+ foreach (var file in dir.GetFiles())
+ {
+ xmlInfo.Add(new XElement("file", new XAttribute("name", file.Name)));
+ }
+ //get subdirectories
+ var subdirectories = dir.GetDirectories().ToList().OrderBy(d => d.Name);
+ foreach (var subDir in subdirectories)
+ {
+ xmlInfo.Add(CreateSubdirectoryXML(subDir));
+ }
+ return xmlInfo;
+ }
+
+ public static XElement CreateSubdirectoryXML(DirectoryInfo dir)
+ {
+ //get directories
+ var xmlInfo = new XElement("folder", new XAttribute("name", dir.Name));
+ //get all the files first
+ foreach (var file in dir.GetFiles())
+ {
+ xmlInfo.Add(new XElement("file", new XAttribute("name", file.Name)));
+ }
+ //get subdirectories
+ var subdirectories = dir.GetDirectories().ToList().OrderBy(d => d.Name);
+ foreach (var subDir in subdirectories)
+ {
+ xmlInfo.Add(CreateSubdirectoryXML(subDir));
+ }
+ return xmlInfo;
+ }
+
+ public static bool ReadConfigXml(out string HLFolder, out bool IsNlPack, out string customDiscordStatus, out bool keepLauncherAlive)
+ {
+ XmlDocument doc = new XmlDocument();
+
+ try
+ {
+ doc.Load(Launcher.curDir + Path.DirectorySeparatorChar + Launcher.configName);
+ XmlNodeList nodelist = doc.SelectNodes("/LauncherConfiguration");
+ HLFolder = doc.SelectSingleNode("//HLFolder").InnerText;
+ IsNlPack = doc.SelectSingleNode("//NLPack").InnerText == "True";
+ customDiscordStatus = doc.SelectSingleNode("//DiscordStatus").InnerText;
+ keepLauncherAlive = doc.SelectSingleNode("//keeplauncherAlive").InnerText == "True";
+ return true;
+ }
+ catch
+ {
+ HLFolder = "";
+ IsNlPack = false;
+ customDiscordStatus = "Gather forming";
+ keepLauncherAlive = true;
+ return false;
+ }
+
+ }
+
+ public static bool CreateConfigXml()
+ {
+ var xmlInfo = new XElement("LauncherConfiguration");
+ xmlInfo.Add(new XElement("HLFolder", Launcher.HLFolder));
+ xmlInfo.Add(new XElement("NLPack", "False"/*Launcher.IsNLPack().ToString()*/)); //doesnt matter anymore, also, can't use this function cause it asks for the config file...
+ xmlInfo.Add(new XElement("DiscordStatus", Launcher.discordCustomStatus));
+ xmlInfo.Add(new XElement("keeplauncherAlive", Launcher.keepLauncherAlive.ToString()));
+
+ var doc = new XDocument(new XDeclaration("1.0", "UTF-8", "yes"), xmlInfo);
+
+ try
+ {
+ File.WriteAllText(Launcher.curDir + Path.DirectorySeparatorChar + Launcher.configName, doc.ToString()); //write the new voidy ignore manifest
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ }
+}
diff --git a/NaturalLauncher/nsl_icon.ico b/NaturalLauncher/nsl_icon.ico
new file mode 100644
index 0000000..aad2710
Binary files /dev/null and b/NaturalLauncher/nsl_icon.ico differ
diff --git a/NaturalLauncher/packages.config b/NaturalLauncher/packages.config
new file mode 100644
index 0000000..4a51347
--- /dev/null
+++ b/NaturalLauncher/packages.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/NslKey.cer b/NslKey.cer
new file mode 100644
index 0000000..1e26daa
Binary files /dev/null and b/NslKey.cer differ
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0592f4a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,32 @@
+# NaturalLauncher
+
+*Natural selection launcher and updater. Allows for quick update of the natural selection folder.
+
+
+#TO BUILD FROM SOURCES
+*If you are building from sources, I imagine you don't want the .exe file to permanantly check for online consistency (public key required) nor self update.
+*In order to do so, simply go in SelfUpdater.cs and set the variable "isSelfUpdating" to false.
+*You are basicaly good to go !
+
+#CHANGE THE ONLINE FILES THE LAUNCHER REFERS TO
+*If you want you own files online, simply change, in the configuration setting of the natural launcher projet in VS, the IndexURL to your custom URL
+*as well as the GameUrl to the game URL (sounds logical enough ?)
+
+#TO COMMIT A NEW NS VERSION / PATCH
+
+##FROM THE LAUNCHER:
+*1 - Launch the launcher (tricky sentence)
+*2 - Verify it has the right half life folder target
+*3 - Right click in the launcher
+*4 - Build Manifest
+
+##FROM THE MANIFESTBUILDER.EXE
+*1 - Place the manifestbuilder.exe app in the correct folder
+*2 - Execute it
+*3 - Enjoy
+*4 - Really do it
+
+##THEN
+*5 - Upload files in the "Game" Folder of the ftp
+*6 - Upload the manifest in the root folder.
+*7 - Update the app.version with the new number in root folder
\ No newline at end of file