diff --git a/installer/RePlaysTV-Installer/CommandLineProcess.cs b/installer/RePlaysTV-Installer/CommandLineProcess.cs
new file mode 100644
index 0000000..c23fc4a
--- /dev/null
+++ b/installer/RePlaysTV-Installer/CommandLineProcess.cs
@@ -0,0 +1,156 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using RePlaysTV_Installer.Views;
+
+namespace RePlaysTV_Installer
+{
+ internal sealed class CommandLineProcess : IDisposable
+ {
+ private readonly string _workDirectory;
+ private bool _startImport;
+
+ public CommandLineProcess(string workDirectory)
+ {
+ _workDirectory = string.IsNullOrEmpty(workDirectory) ? null : workDirectory;
+ Process = new Process
+ {
+ StartInfo =
+ {
+ FileName = "cmd.exe",
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ RedirectStandardInput = true,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ StandardOutputEncoding = Encoding.UTF8,
+ StandardErrorEncoding = Encoding.UTF8
+ }
+ };
+
+ Process.OutputDataReceived += SortOutputHandler;
+ Process.ErrorDataReceived += SortOutputHandler;
+ }
+
+ private Process Process { get; }
+
+ private StreamWriter StreamWriter { get; set; }
+
+ public void Dispose()
+ {
+ Process?.Dispose();
+ StreamWriter?.Dispose();
+ }
+
+ public async void SetupStreamWriterWithEncoding(string encoding = "chcp 65001")
+ {
+ if (string.IsNullOrWhiteSpace(encoding)) throw new ArgumentException(nameof(encoding));
+
+ StreamWriter = Process?.StandardInput;
+
+ if (StreamWriter == null)
+ throw new InvalidOperationException(
+ "There was a problem with initializing the stream writer for the command line process.");
+
+ await StreamWriter?.WriteLineAsync(encoding);
+ }
+
+ public void StartCommandLineProcess()
+ {
+ // run as console app
+ Main.Log("Initializing Updater");
+
+ if (Process == null)
+ throw new InvalidOperationException(
+ "The command line for the whole execution was not initialized correct.");
+
+ Process.Start();
+ Process.BeginOutputReadLine();
+ Process.BeginErrorReadLine();
+ }
+
+ private async void SortOutputHandler(object sendingProcess, DataReceivedEventArgs dataReceivedEventArgs)
+ {
+ if (string.IsNullOrEmpty(dataReceivedEventArgs.Data))
+ {
+ return;
+ }
+
+ if (dataReceivedEventArgs.Data.Contains("All rights reserved."))
+ {
+ Main.Log("Ready");
+ }
+ else if (dataReceivedEventArgs.Data.Contains("We will now attempt to import: ") && !_startImport)
+ {
+ //part two of installation
+ _startImport = true;
+ await Installer.StartImport(StreamWriter);
+ Main.Log("=======================================");
+ Main.Log("=======================================");
+ Main.Log("=======================================");
+ Main.Log("This next Process will take awhile (with no sign of progress)... Please be patient.");
+ ;
+ }
+ else
+ {
+ if (dataReceivedEventArgs.Data.Contains("npm install") ||
+ dataReceivedEventArgs.Data.Contains("electron-forge package") &&
+ !dataReceivedEventArgs.Data.Contains("\"electron-forge package\"") ||
+ dataReceivedEventArgs.Data.Contains("asar extract"))
+ {
+ Main.Log("=======================================");
+ Main.Log("=======================================");
+ Main.Log("=======================================");
+ Main.Log(
+ $"[{DateTime.Now:h:mm:ss tt}] This next Process will take awhile (with no sign of progress)... Please be patient.");
+ ;
+ }
+
+ if (dataReceivedEventArgs.Data.Contains("Thanks for using ") && dataReceivedEventArgs.Data.Contains("electron-forge"))
+ {
+ await Installer.StartModify(StreamWriter);
+ }
+
+ if (dataReceivedEventArgs.Data.Contains("npm ERR!") ||
+ dataReceivedEventArgs.Data.Contains("unhandled error") ||
+ dataReceivedEventArgs.Data.Contains("Error: "))
+ {
+ var message =
+ "An unhandled error has occurred during the install, It is possible that the installation has failed.\nTry restarting your computer and turn off anti-virus before installing.\n\nReport this issue by copying the logs and sending it to a developer.";
+ Main.ShowMessageBox(message);
+ }
+
+ if (dataReceivedEventArgs.Data.Contains("'nodejs-portable.exe' is not recognized"))
+ {
+ var message =
+ "'nodejs-portable.exe' is missing from the working directory.\n\nMake sure you properly extracted the installer to a folder.";
+ Main.ShowMessageBox(message);
+ }
+
+ if (dataReceivedEventArgs.Data.Contains(">exit"))
+ {
+ Main.Log("=======================================");
+ Main.Log("=======================================");
+ Main.Log("=======================================");
+ Main.Log("Installation Complete!");
+ Main.InstallComplete();
+ }
+
+ Main.Log(dataReceivedEventArgs.Data);
+ }
+ }
+
+ public async Task ExecuteMainTaskAsync()
+ {
+ if ((StreamWriter == null) | !await Installer.DownloadPlaysSetup(_workDirectory))
+ {
+ return;
+ }
+
+ Installer.ListInstalledAntivirusProducts();
+ await Installer.StartExtract(StreamWriter, _workDirectory);
+ }
+ }
+}
\ No newline at end of file
diff --git a/installer/RePlaysTV-Installer/Helper/AsynchronousFileHelper.cs b/installer/RePlaysTV-Installer/Helper/AsynchronousFileHelper.cs
new file mode 100644
index 0000000..ca428fd
--- /dev/null
+++ b/installer/RePlaysTV-Installer/Helper/AsynchronousFileHelper.cs
@@ -0,0 +1,67 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace RePlaysTV_Installer.Helper
+{
+ public static class AsynchronousFileHelper
+ {
+ ///
+ /// This is the same default buffer size as
+ /// and .
+ ///
+ private const int DefaultBufferSize = 4096;
+
+ ///
+ /// Indicates that
+ /// 1. The file is to be used for asynchronous reading.
+ /// 2. The file is to be accessed sequentially from beginning to end.
+ ///
+ private const FileOptions DefaultOptions = FileOptions.Asynchronous | FileOptions.SequentialScan;
+
+ public static async Task ReadAllLinesAsync(string path)
+ {
+ return await ReadAllLinesAsync(path, Encoding.UTF8);
+ }
+
+ public static async Task ReadAllLinesAsync(string path, Encoding encoding)
+ {
+ var lines = new List();
+
+ // Open the FileStream with the same FileMode, FileAccess
+ // and FileShare as a call to File.OpenText would've done.
+ using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultBufferSize,
+ DefaultOptions))
+ using (var reader = new StreamReader(stream, encoding))
+ {
+ string line;
+ while ((line = await reader.ReadLineAsync()) != null) lines.Add(line);
+ }
+
+ return lines.ToArray();
+ }
+
+ public static async Task WriteAllLinesAsync(string path, List linesToWrite)
+ {
+ await WriteAllLinesAsync(path, Encoding.UTF8, linesToWrite.ToArray());
+ }
+
+ public static async Task WriteAllLinesAsync(string path, IEnumerable linesToWrite)
+ {
+ await WriteAllLinesAsync(path, Encoding.UTF8, linesToWrite);
+ }
+
+ public static async Task WriteAllLinesAsync(string path, Encoding encoding, IEnumerable linesToWrite)
+ {
+ // Open the FileStream with the same FileMode, FileAccess
+ // and FileShare as a call to File.OpenText would've done.
+ using (var stream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None,
+ DefaultBufferSize, DefaultOptions))
+ using (var writer = new StreamWriter(stream, encoding))
+ {
+ foreach (var line in linesToWrite) await writer.WriteLineAsync(line);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/installer/RePlaysTV-Installer/Installer.cs b/installer/RePlaysTV-Installer/Installer.cs
index 3b89ce9..4a8e251 100644
--- a/installer/RePlaysTV-Installer/Installer.cs
+++ b/installer/RePlaysTV-Installer/Installer.cs
@@ -1,227 +1,292 @@
using System;
-using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Management;
using System.Net;
using System.Security.Cryptography;
-using System.Text;
-using System.Threading;
using System.Threading.Tasks;
-using System.Windows.Forms;
+using RePlaysTV_Installer.Helper;
+using RePlaysTV_Installer.Settings;
+using RePlaysTV_Installer.Views;
-namespace RePlaysTV_Installer {
- class Installer {
- public static string rePlaysDirectory = Environment.GetEnvironmentVariable("LocalAppData") + "\\RePlays";
- public static Dictionary installSettings =
- new Dictionary ()
+namespace RePlaysTV_Installer
+{
+ internal sealed class Installer
+ {
+ public static void ListInstalledAntivirusProducts()
{
- { "playsSetupUrl", "https://web.archive.org/web/20191212211927if_/https://app-updates.plays.tv/builds/PlaysSetup.exe" },
- { "ltcVersion", "0.54.7" },
- { "cleanInstall", true },
- { "deleteTemp", false },
- { "ignoreChecksum", false },
- };
- public static void ListInstalledAntivirusProducts(RichTextBox richTextBox1 = null) {
using (var searcher = new ManagementObjectSearcher(@"\\" +
- Environment.MachineName +
- @"\root\SecurityCenter2",
- "SELECT * FROM AntivirusProduct")) {
+ Environment.MachineName +
+ @"\root\SecurityCenter2",
+ "SELECT * FROM AntivirusProduct"))
+ {
var searcherInstance = searcher.Get();
- var msg = "Detected installed antivirus(es), may conflict with the installation: ";
- if(searcherInstance.Count < 1) {
- foreach (var instance in searcherInstance) {
- msg = msg + instance["displayName"].ToString() + ", ";
+ Main.Log("Detected installed antivirus(es), may conflict with the installation: ");
+
+ if (searcherInstance.Count < 1)
+ {
+ foreach (var instance in searcherInstance)
+ {
+ Main.Log($"{instance["displayName"]},");
}
- }
- else {
- msg = "No installed antivirus detected.";
+
+ return;
}
- Log(msg, richTextBox1);
+ Main.Log("No installed antivirus detected.");
}
}
- public static void ListFilesInDir(StreamWriter SW, string dir, RichTextBox richTextBox1 = null) {
- Log("Displaying tree view of '" + dir + "' for debugging purposes:", richTextBox1);
- SW.WriteLine("tree \"" + dir + "\" /f");
+ public async Task ListFilesInDir(StreamWriter SW, string dir)
+ {
+ Main.Log("Displaying tree view of '" + dir + "' for debugging purposes:");
+ await SW.WriteLineAsync("tree \"" + dir + "\" /f");
}
- public static async Task DownloadSetup(RichTextBox richTextBox1 = null, string workDirectory = null) { //part one of installation
- if (workDirectory == null) workDirectory = Directory.GetCurrentDirectory();
-
- var correctHash = "3a7cea84d50ad2c31a79e66f5c3f3b8d";
+ public static async Task DownloadPlaysSetup(string workDirectory = null)
+ {
+ //part one of installation
+ workDirectory = workDirectory ?? Directory.GetCurrentDirectory();
+ var correctHash = InstallerSettings.GetInstallerSetting(InstallerSetting.CorrectPlaysSetupHash);
// Checksum
- if (File.Exists(workDirectory + "\\PlaysSetup.exe")) {
- if ((bool)installSettings["ignoreChecksum"] == false) {
- bool checksumPass = false;
- using (var md5 = MD5.Create()) {
- using (var stream = File.OpenRead(workDirectory + "\\PlaysSetup.exe")) {
- var hash = md5.ComputeHash(stream);
- var hashAsString = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
-
- if (correctHash == hashAsString) {
- checksumPass = true;
- Log("PlaysSetup.exe passed MD5 checksum: " + BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(), richTextBox1);
- } else {
- checksumPass = false;
- Log("PlaysSetup.exe did not pass MD5 checksum!!!", richTextBox1);
- }
+ if (File.Exists(workDirectory + "\\PlaysSetup.exe") &&
+ !InstallerSettings.GetInstallerSetting(InstallerSetting.IgnoreChecksum))
+ {
+ using (var md5 = MD5.Create())
+ {
+ Main.Log("Check if executable has correct hash.");
+ using (var stream = File.OpenRead(workDirectory + "\\PlaysSetup.exe"))
+ {
+ var hashAsString = BitConverter.ToString(md5.ComputeHash(stream)).Replace("-", "")
+ .ToLowerInvariant();
+ if (correctHash == hashAsString)
+ {
+ Main.Log($"PlaysSetup.exe has the correct MD5: {hashAsString}");
+ return true;
}
}
- if (!checksumPass)
- File.Delete(workDirectory + "\\PlaysSetup.exe");
- else return;
- } else return;
+ }
+
+ // PlaysSetup.exe did not pass MD5 checksum!!!
+ Main.Log(
+ $"The checksum found on PlaysSetup was not the one needed.{Environment.NewLine}The file will be deleted.{Environment.NewLine}Please restart installation.");
+ File.Delete(workDirectory + "\\PlaysSetup.exe");
+ return false;
}
- Log("PlaysSetup.exe missing or failed checksum, starting download", richTextBox1);
- using (var client = new WebClient()) {
- client.DownloadProgressChanged += (o, args) => {
- Log("Downloading PlaysSetup.exe @ web.archive.org: " + args.BytesReceived + " / 145310344 Bytes", richTextBox1);
+ Main.Log("PlaysSetup.exe missing or failed checksum, starting download");
+ using (var client = new WebClient())
+ {
+ var playSetupExecutableSizeInBytes =
+ InstallerSettings.GetInstallerSetting(InstallerSetting.PlaySetupExecutableSizeInBytes);
+ var playsSetupExecutableSizeInMb = Math.Round(playSetupExecutableSizeInBytes / 1000000, 2);
+ client.DownloadProgressChanged += (o, args) =>
+ {
+ var bytesReceivedInMegabyte = args.BytesReceived / 1000000;
+ var progressInPercent =
+ decimal.Round(decimal.Divide(bytesReceivedInMegabyte, playsSetupExecutableSizeInMb) * 100, 2);
+ Main.Log(
+ $"Downloading PlaysSetup.exe @ web.archive.org: {bytesReceivedInMegabyte}/{playsSetupExecutableSizeInMb} MB {progressInPercent}%");
};
- client.DownloadFileCompleted += async (o, args) => {
- Log("Finished downloading PlaysSetup.exe, doing a checksum", richTextBox1);
- await DownloadSetup(richTextBox1, workDirectory);
+
+ client.DownloadFileCompleted += async (o, args) =>
+ {
+ Main.Log("Finished downloading PlaysSetup.exe, doing a checksum");
+ await DownloadPlaysSetup(workDirectory);
};
+
await client.DownloadFileTaskAsync(
- new Uri((string) installSettings["playsSetupUrl"]),
+ new Uri(InstallerSettings.GetInstallerSetting(InstallerSetting.PlaysSetupUrl)),
workDirectory + "\\PlaysSetup.exe");
}
+
+ return false;
}
- public static void StartExtract(StreamWriter SW, string workDirectory=null) { //part one of installation
- if (workDirectory == null) workDirectory = Directory.GetCurrentDirectory();
- else SW.WriteLine("cd /d \"" + workDirectory + "\"");
+ public static async Task StartExtract(StreamWriter sw, string workDirectory = null)
+ {
+ //part one of installation
+ if (workDirectory != null)
+ {
+ await sw.WriteLineAsync("cd /d \"" + workDirectory + "\"");
+ }
+
+ workDirectory = workDirectory ?? Directory.GetCurrentDirectory();
+ Main.Log($"Starting to work on the folder {workDirectory}");
- SW.WriteLine("nodejs-portable.exe");
- if (Directory.Exists(workDirectory + "\\temp") && (bool)installSettings["cleanInstall"] == true) {
- SW.WriteLine("rd /s /q temp");
+ await sw.WriteLineAsync("nodejs-portable.exe");
+ if (Directory.Exists(workDirectory + "\\temp") &&
+ InstallerSettings.GetInstallerSetting(InstallerSetting.CleanInstall))
+ {
+ await sw.WriteLineAsync("rd /s /q temp");
}
- SW.WriteLine("mkdir temp");
- SW.WriteLine("7z e PlaysSetup.exe -o.\\PlaysSetup -aos");
- SW.WriteLine("copy /Y .\\PlaysSetup\\Update.exe .\\Update.exe");
- SW.WriteLine("7z x .\\PlaysSetup\\Plays-3.0.0-full.nupkg -o.\\PlaysSetup\\Plays-3.0.0-full -aos");
- SW.WriteLine("asar extract .\\PlaysSetup\\Plays-3.0.0-full\\lib\\net45\\resources\\app.asar temp");
- SW.WriteLine("cd temp");
- SW.WriteLine("npm init -f");
- SW.WriteLine("npm install");
- SW.WriteLine("electron-forge import");
+
+ await sw.WriteLineAsync("mkdir temp");
+ await sw.WriteLineAsync("7z e PlaysSetup.exe -o.\\PlaysSetup -aos");
+ await sw.WriteLineAsync("copy /Y .\\PlaysSetup\\Update.exe .\\Update.exe");
+ await sw.WriteLineAsync(
+ "7z x .\\PlaysSetup\\Plays-3.0.0-full.nupkg -o.\\PlaysSetup\\Plays-3.0.0-full -aos");
+ await sw.WriteLineAsync(
+ "asar extract .\\PlaysSetup\\Plays-3.0.0-full\\lib\\net45\\resources\\app.asar temp");
+ await sw.WriteLineAsync("cd temp");
+ await sw.WriteLineAsync("npm init -f");
+ await sw.WriteLineAsync("npm install");
+ await sw.WriteLineAsync("electron-forge import");
}
- public static void StartImport(StreamWriter SW) { //part two of installation
- Thread.Sleep(5000);
- SW.WriteLine("y");
- Thread.Sleep(5000);
- SW.WriteLine("y");
- Thread.Sleep(5000);
- SW.WriteLine("src/main/main.js");
- Thread.Sleep(5000);
- SW.WriteLine("n");
- Thread.Sleep(5000);
+
+ public static async Task StartImport(StreamWriter sw)
+ {
+ //part two of installation
+ await Task.Delay(5000);
+ await sw.WriteLineAsync("y");
+ await Task.Delay(5000);
+ await sw.WriteLineAsync("y");
+ await Task.Delay(5000);
+ await sw.WriteLineAsync("src/main/main.js");
+ await Task.Delay(5000);
+ await sw.WriteLineAsync("n");
+ await Task.Delay(5000);
}
- public static void StartModify(StreamWriter SW, string VERSION, string workDirectory = null, RichTextBox richTextBox1 = null) { //part three of installation
+
+ public static async Task StartModify(StreamWriter sw, string workDirectory = null)
+ {
+ //part three of installation
if (workDirectory == null) workDirectory = Directory.GetCurrentDirectory();
- SW.WriteLine("npm install electron-prebuilt-compile@4.0.0 --save-dev --save-exact");
- SW.WriteLine("cd ..");
- SW.WriteLine("rd /s /q .\\temp\\.cache");
- SW.WriteLine("mkdir .\\temp\\resources\\auger\\replays");
- SW.WriteLine("robocopy /E /NP /MT .\\src .\\temp\\resources\\auger\\replays");
+ await sw.WriteLineAsync("npm install electron-prebuilt-compile@4.0.0 --save-dev --save-exact");
+ await sw.WriteLineAsync("cd ..");
+ await sw.WriteLineAsync("rd /s /q .\\temp\\.cache");
+ await sw.WriteLineAsync("mkdir .\\temp\\resources\\auger\\replays");
+ await sw.WriteLineAsync("robocopy /E /NP /MT .\\src .\\temp\\resources\\auger\\replays");
//--------------------------------------
//start modifying original plays files
//--------------------------------------
//copying replaceables
- SW.WriteLine("robocopy /E /NP /MT .\\src-patch\\src .\\temp\\src");
+ await sw.WriteLineAsync("robocopy /E /NP /MT .\\src-patch\\src .\\temp\\src");
//set version
- ModifyFileAtLine("log.info(`Version: " + VERSION + "`);", workDirectory + "\\temp\\src\\main\\main.js", 36, richTextBox1);
-
- int counter = 1;
- string line;
- StreamReader configFile = new StreamReader(workDirectory + "\\src-patch\\config.txt");
- while ((line = configFile.ReadLine()) != null) {
- if (!line.StartsWith("#") && !String.IsNullOrEmpty(line)) { // if it is not a comment
- if(line.StartsWith("ModifyFileAtLine") || line.StartsWith("AppendToFileAtLine")) {
- string command = "ModifyFileAtLine";
- if (line.StartsWith("AppendToFileAtLine"))
- command = "AppendToFileAtLine";
-
- string fileName = line.Split(new string[] { "\"" }, 3, StringSplitOptions.None)[1];
- string lineNumber = line.Replace(command + " \"" + fileName + "\" ", ""); lineNumber = lineNumber.Substring(0, lineNumber.IndexOf(' '));
- string newLine = line.Replace(command + " \"" + fileName + "\" " + lineNumber + " ", "");
-
- if(lineNumber.Contains("-") && command == "ModifyFileAtLine") {
- int start = Int32.Parse(lineNumber.Split('-')[0]);
- int end = Int32.Parse(lineNumber.Split('-')[1]);
-
- for (int i = start; i <= end; i++) {
- ModifyFileAtLine(newLine, workDirectory + "\\temp" + fileName, i, richTextBox1);
- counter++;
- }
- } else {
- if(command == "AppendToFileAtLine")
- AppendToFileAtLine(newLine, workDirectory + "\\temp" + fileName, Int32.Parse(lineNumber), richTextBox1);
- else
- ModifyFileAtLine(newLine, workDirectory + "\\temp" + fileName, Int32.Parse(lineNumber), richTextBox1);
+ await ModifyFileAtLineAsync(
+ "log.info(`Version: " + InstallerSettings.GetInstallerSetting(InstallerSetting.Version) + "`);",
+ workDirectory + "\\temp\\src\\main\\main.js", 36);
+
+ var counter = 1;
+ using (var configFileReader = new StreamReader(workDirectory + "\\src-patch\\config.txt"))
+ {
+ while (!configFileReader.EndOfStream)
+ {
+ var readLine = await configFileReader.ReadLineAsync();
+ if (readLine.StartsWith("#") || string.IsNullOrWhiteSpace(readLine))
+ {
+ continue; // if it is not a comment
+ }
+
+ if (!readLine.StartsWith("ModifyFileAtLine") && !readLine.StartsWith("AppendToFileAtLine"))
+ {
+ continue;
+ }
+
+ var command = "ModifyFileAtLine";
+ if (readLine.StartsWith("AppendToFileAtLine"))
+ {
+ command = "AppendToFileAtLine";
+ }
+
+ var fileName = readLine.Split(new[] {"\""}, 3, StringSplitOptions.None)[1];
+ var lineNumber = readLine.Replace(command + " \"" + fileName + "\" ", "");
+ lineNumber = lineNumber.Substring(0, lineNumber.IndexOf(' '));
+ var newLine = readLine.Replace(command + " \"" + fileName + "\" " + lineNumber + " ", "");
+
+ if (lineNumber.Contains("-") && command == "ModifyFileAtLine")
+ {
+ var start = int.Parse(lineNumber.Split('-')[0]);
+ var end = int.Parse(lineNumber.Split('-')[1]);
+
+ for (var i = start; i <= end; i++)
+ {
+ await ModifyFileAtLineAsync(newLine, workDirectory + "\\temp" + fileName, i);
counter++;
}
}
+ else
+ {
+ if (command == "AppendToFileAtLine")
+ await AppendToFileAtLine(newLine, workDirectory + "\\temp" + fileName,
+ int.Parse(lineNumber));
+ else
+ await ModifyFileAtLineAsync(newLine, workDirectory + "\\temp" + fileName,
+ int.Parse(lineNumber));
+ counter++;
+ }
}
+
+ Main.Log("Number of changes: " + counter);
}
- Log("Number of changes: " + counter, richTextBox1);
- configFile.Close();
- StartPackage(SW, VERSION, workDirectory);
+ await StartPackage(sw, workDirectory);
}
- public static void StartPackage(StreamWriter SW, string VERSION, string workDirectory = null) { //part four of installation
- ModifyFileAtLine("" + VERSION + "-full", workDirectory + "\\Plays.nuspec", 5);
- SW.WriteLine("copy /Y nuget.exe temp");
- SW.WriteLine("copy /Y Plays.nuspec temp");
- SW.WriteLine("cd temp");
- SW.WriteLine("npm run package");
- SW.WriteLine("robocopy /E /NP /MT ..\\PlaysSetup\\Plays-3.0.0-full\\lib\\net45\\resources\\ltc .\\out\\Plays-win32-ia32\\resources\\ltc");
- SW.WriteLine("rename .\\out\\Plays-win32-ia32 net45");
- SW.WriteLine("nuget.exe pack");
- StartInstall(SW, VERSION, workDirectory);
+ private static async Task StartPackage(TextWriter textWriter, string workDirectory = null)
+ {
+ //part four of installation
+ await ModifyFileAtLineAsync(
+ $"{InstallerSettings.GetInstallerSetting(InstallerSetting.Version)}-full",
+ workDirectory + "\\Plays.nuspec", 5);
+ await textWriter.WriteLineAsync("copy /Y nuget.exe temp");
+ await textWriter.WriteLineAsync("copy /Y Plays.nuspec temp");
+ await textWriter.WriteLineAsync("cd temp");
+ await textWriter.WriteLineAsync("npm run package");
+ await textWriter.WriteLineAsync(
+ "robocopy /E /NP /MT ..\\PlaysSetup\\Plays-3.0.0-full\\lib\\net45\\resources\\ltc .\\out\\Plays-win32-ia32\\resources\\ltc");
+ await textWriter.WriteLineAsync("rename .\\out\\Plays-win32-ia32 net45");
+ await textWriter.WriteLineAsync("nuget.exe pack");
+ await StartInstallAsync(textWriter, workDirectory);
}
- public static void StartInstall(StreamWriter SW, string VERSION, string workDirectory = null) { //part five of installation
- SW.WriteLine("copy /Y Plays." + VERSION + "-full.nupkg ..");
- SW.WriteLine("cd ..");
- SW.WriteLine("del /f RELEASES");
- if (workDirectory == rePlaysDirectory) {
- SW.WriteLine("Update.exe --install=.\\");
- } else {
- SW.WriteLine("echo WARNING: Current work directory is '" + workDirectory + "', proper work directory should be at '" + rePlaysDirectory + "', skipping completion install...");
+ private static async Task StartInstallAsync(TextWriter textWriter, string workDirectory = null)
+ {
+ //part five of installation
+ await textWriter.WriteLineAsync("copy /Y Plays." +
+ InstallerSettings.GetInstallerSetting(InstallerSetting.Version) +
+ "-full.nupkg ..");
+ await textWriter.WriteLineAsync("cd ..");
+ await textWriter.WriteLineAsync("del /f RELEASES");
+
+ if (workDirectory == InstallerSettings.GetInstallerSetting(InstallerSetting.RePlaysDirectory))
+ {
+ await textWriter.WriteLineAsync("Update.exe --install=.\\");
}
- if((bool)installSettings["deleteTemp"] == true) {
- SW.WriteLine("rd /s /q temp");
+ else
+ {
+ await textWriter.WriteLineAsync(
+ $"echo WARNING: Current work directory is '{workDirectory}', proper work directory should be at '{InstallerSettings.GetInstallerSetting(InstallerSetting.RePlaysDirectory)}', skipping completion install...");
+ }
+
+ if (InstallerSettings.GetInstallerSetting(InstallerSetting.DeleteTemp))
+ {
+ await textWriter.WriteLineAsync("rd /s /q temp");
}
- SW.WriteLine("exit");
- }
- public static void ModifyFileAtLine(string newText, string fileName, int line_to_edit, RichTextBox richTextBox1 = null) {
- string[] arrLine = File.ReadAllLines(fileName);
- arrLine[line_to_edit - 1] = newText;
- File.WriteAllLines(fileName, arrLine);
- Log(fileName + ">>> Writing to line " + line_to_edit + ": " + newText, richTextBox1);
+ await textWriter.WriteLineAsync("exit");
}
- public static void AppendToFileAtLine(string newText, string fileName, int line_to_edit, RichTextBox richTextBox1 = null) {
- var arrLine = File.ReadAllLines(fileName).ToList();
- arrLine.Insert(line_to_edit + 1, newText);
- File.WriteAllLines(fileName, arrLine);
- Log(fileName + ">>> Writing to line " + line_to_edit + ": " + newText, richTextBox1);
+ private static async Task ModifyFileAtLineAsync(string newText, string fileName, int lineToEdit)
+ {
+ var allTextFromFile = await AsynchronousFileHelper.ReadAllLinesAsync(fileName);
+ allTextFromFile[lineToEdit - 1] = newText;
+ await AsynchronousFileHelper.WriteAllLinesAsync(fileName, allTextFromFile);
+ Main.Log($"{fileName}>>> Writing to line {lineToEdit}: {newText}");
}
- public static void Log(string msg, RichTextBox richTextBox1 = null) {
- if (richTextBox1 != null) {
- richTextBox1.AppendText(Environment.NewLine + "[" + DateTime.Now.ToString("h:mm:ss tt") + "] " + msg);
- } Console.WriteLine(msg);
+ private static async Task AppendToFileAtLine(string newText, string fileName, int lineToEdit)
+ {
+ var allTextFromFile = (await AsynchronousFileHelper.ReadAllLinesAsync(fileName)).ToList();
+ var arrLine = File.ReadAllLines(fileName).ToList();
+ arrLine.Insert(lineToEdit + 1, newText);
+ await AsynchronousFileHelper.WriteAllLinesAsync(fileName, allTextFromFile);
+ Main.Log($"{fileName}>>> Writing to line {lineToEdit}: {newText}");
}
}
-}
+}
\ No newline at end of file
diff --git a/installer/RePlaysTV-Installer/Main.cs b/installer/RePlaysTV-Installer/Main.cs
deleted file mode 100644
index dcbf891..0000000
--- a/installer/RePlaysTV-Installer/Main.cs
+++ /dev/null
@@ -1,146 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Data;
-using System.Diagnostics;
-using System.Drawing;
-using System.IO;
-using System.Reflection;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Threading;
-using System.Windows.Forms;
-
-namespace RePlaysTV_Installer {
- public partial class Main : Form {
- public string VERSION;
-
- public static Process p;
- public static StreamWriter SW;
- public static Main mainForm;
- public static Options optionsForm = new Options();
- public Main(string _VERSION) {
- VERSION = _VERSION;
-
- InitializeComponent();
- mainForm = this;
- }
- private void Main_Load(object sender, EventArgs e) {
- mainForm.Text = "RePlaysTV " + VERSION + " Installer";
- mainForm.WindowState = FormWindowState.Minimized;
- mainForm.Show();
- mainForm.WindowState = FormWindowState.Normal;
-
- p = new Process();
-
- p.StartInfo.FileName = "cmd.exe";
- p.StartInfo.UseShellExecute = false;
- p.StartInfo.CreateNoWindow = true;
- p.StartInfo.RedirectStandardInput = true;
- p.StartInfo.RedirectStandardOutput = true;
- p.StartInfo.RedirectStandardError = true;
- p.StartInfo.StandardOutputEncoding = Encoding.UTF8;
- p.StartInfo.StandardErrorEncoding = Encoding.UTF8;
-
- p.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
- p.ErrorDataReceived += new DataReceivedEventHandler(SortOutputHandler);
-
- p.Start();
-
- SW = p.StandardInput;
- SW.WriteLine("chcp 65001"); //set encoding
-
- p.BeginOutputReadLine();
- p.BeginErrorReadLine();
- }
-
- private async void Button1_Click(object sender, EventArgs e) {
- mainForm.TopMost = true;
- DialogResult dr1 = MessageBox.Show("This automated process can take up to 10 minutes or more.\n" +
- "Please have at least ~1.5GB of disk space available." +
- "\nPress Yes to start install.", "RePlaysTV Installer", MessageBoxButtons.YesNoCancel,MessageBoxIcon.Information);
- if (dr1 == DialogResult.Yes) {
- await Installer.DownloadSetup(mainForm.richTextBox1);
- Installer.ListInstalledAntivirusProducts(mainForm.richTextBox1);
- Installer.StartExtract(SW);
- }
- mainForm.TopMost = false;
- }
-
- private void Button2_Click(object sender, EventArgs e) {
- optionsForm.ShowDialog();
- }
-
- private void Button3_Click(object sender, EventArgs e) {
- Clipboard.SetText(mainForm.richTextBox1.Text);
- }
- private void InstallComplete() {
- mainForm.TopMost = true;
- MessageBox.Show("Installation Complete!", "RePlaysTV Installer", MessageBoxButtons.OK, MessageBoxIcon.Information);
- mainForm.TopMost = false;
- }
- private static bool startImport = false;
- private static void SortOutputHandler(object sendingProcess, DataReceivedEventArgs outLine) {
- if (!String.IsNullOrEmpty(outLine.Data)) {
- if (mainForm.richTextBox1.InvokeRequired) {
- mainForm.richTextBox1.Invoke(new MethodInvoker(delegate {
- if (outLine.Data.Contains("All rights reserved.")) {
- mainForm.richTextBox1.Text = "[" + DateTime.Now.ToString("h:mm:ss tt") + "] Ready";
- } else if (outLine.Data.Contains("We will now attempt to import: ") && !startImport) { //part two of installation
- var enterThread = new Thread(
- new ThreadStart(
- () => {
- Installer.StartImport(SW);
- mainForm.Invoke(new MethodInvoker(delegate {
- mainForm.richTextBox1.AppendText(Environment.NewLine + "=======================================");
- mainForm.richTextBox1.AppendText(Environment.NewLine + "=======================================");
- mainForm.richTextBox1.AppendText(Environment.NewLine + "=======================================");
- mainForm.richTextBox1.AppendText(Environment.NewLine + "[" + DateTime.Now.ToString("h:mm:ss tt") + "] This next process will take awhile (with no sign of progress)... Please be patient.");
- }));
- }
- ));
- startImport = true;
- enterThread.Start();
- } else {
- if (outLine.Data.Contains("npm install") || (outLine.Data.Contains("electron-forge package") && !outLine.Data.Contains("\"electron-forge package\"")) || outLine.Data.Contains("asar extract")) {
- mainForm.richTextBox1.AppendText(Environment.NewLine + "=======================================");
- mainForm.richTextBox1.AppendText(Environment.NewLine + "=======================================");
- mainForm.richTextBox1.AppendText(Environment.NewLine + "=======================================");
- mainForm.richTextBox1.AppendText(Environment.NewLine + "[" + DateTime.Now.ToString("h:mm:ss tt") + "] This next process will take awhile (with no sign of progress)... Please be patient.");
- }
- if (outLine.Data.Contains("Thanks for using ") && outLine.Data.Contains("electron-forge")) {
- Installer.StartModify(SW, mainForm.VERSION, null, mainForm.richTextBox1);
- }
- if (outLine.Data.Contains("npm ERR!") || outLine.Data.Contains("unhandled error") || outLine.Data.Contains("Error: ")) {
- mainForm.TopMost = true;
- System.Windows.Forms.MessageBox.Show("An unhandled error has occurred during the install, It is possible that the installation has failed.\nTry restarting your computer and turn off anti-virus before installing.\n\nReport this issue by copying the logs and sending it to a developer.");
- mainForm.TopMost = false;
- }
- if (outLine.Data.Contains("'nodejs-portable.exe' is not recognized")) {
- mainForm.TopMost = true;
- System.Windows.Forms.MessageBox.Show("'nodejs-portable.exe' is missing from the working directory.\n\nMake sure you properly extracted the installer to a folder.");
- mainForm.TopMost = false;
- }
- if (outLine.Data.Contains(">exit")) {
- mainForm.richTextBox1.AppendText(Environment.NewLine + "=======================================");
- mainForm.richTextBox1.AppendText(Environment.NewLine + "=======================================");
- mainForm.richTextBox1.AppendText(Environment.NewLine + "=======================================");
- mainForm.richTextBox1.AppendText(Environment.NewLine + "[" + DateTime.Now.ToString("h:mm:ss tt") + "] Installation Complete!");
- mainForm.Invoke(new MethodInvoker(delegate { mainForm.InstallComplete(); }));
- }
- mainForm.richTextBox1.AppendText(Environment.NewLine + "[" + DateTime.Now.ToString("h:mm:ss tt") + "] " + outLine.Data.ToString());
- }
- }));
- }
- }
- }
-
- private void Main_FormClosing(object sender, FormClosingEventArgs e) {
- p.Kill();
- }
-
- private void LinkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) {
- Process.Start("https://github.com/lulzsun/RePlaysTV");
- }
- }
-}
diff --git a/installer/RePlaysTV-Installer/MainProgram.cs b/installer/RePlaysTV-Installer/MainProgram.cs
new file mode 100644
index 0000000..942716f
--- /dev/null
+++ b/installer/RePlaysTV-Installer/MainProgram.cs
@@ -0,0 +1,121 @@
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using RePlaysTV_Installer.Settings;
+using RePlaysTV_Installer.Views;
+
+namespace RePlaysTV_Installer
+{
+ internal static class MainProgram
+ {
+ [STAThread]
+ private static async Task Main(string[] args)
+ {
+ ActivateGlobalExceptionHandling();
+ InstallerSettings.SetInstallerSetting(InstallerSetting.Version, Application.ProductVersion);
+
+ if (args.Length == 0)
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(new Main());
+ return;
+ }
+
+ var workDirectory = args[0]; //args[0] - working dir passed from replays client
+ using (var commandLineProcess = new CommandLineProcess(workDirectory))
+ {
+ commandLineProcess.StartCommandLineProcess();
+ commandLineProcess.SetupStreamWriterWithEncoding();
+
+ await Task.Factory.StartNew(commandLineProcess.ExecuteMainTaskAsync);
+ }
+ }
+
+ private static void ActivateGlobalExceptionHandling()
+ {
+ // Add the event handler for handling UI thread exceptions to the event.
+ Application.ThreadException += UIThreadException;
+
+ // Set the unhandled exception mode to force all Windows Forms errors to go through
+ // our handler.
+ Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
+
+ // Add the event handler for handling non-UI thread exceptions to the event.
+ AppDomain.CurrentDomain.UnhandledException += UnhandledException;
+ }
+
+
+ // Handle the UI exceptions by showing a dialog box, and asking the user whether
+ // or not they wish to abort execution.
+ private static void UIThreadException(object sender, ThreadExceptionEventArgs t)
+ {
+ var result = DialogResult.Cancel;
+ try
+ {
+ result = ShowThreadExceptionDialog("Windows Forms Error", t.Exception);
+ }
+ catch
+ {
+ try
+ {
+ MessageBox.Show("Fatal Windows Forms Error", "Fatal Windows Forms Error", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Stop);
+ }
+ finally
+ {
+ Application.Exit();
+ }
+ }
+
+ // Exits the program when the user clicks Abort.
+ if (result == DialogResult.Abort)
+ {
+ Application.Exit();
+ }
+ }
+
+ // Handle the UI exceptions by showing a dialog box, and asking the user whether
+ // or not they wish to abort execution.
+ // NOTE: This exception cannot be kept from terminating the application - it can only
+ // log the event, and inform the user about it.
+ private static void UnhandledException(object sender, UnhandledExceptionEventArgs e)
+ {
+ try
+ {
+ var ex = (Exception)e.ExceptionObject;
+ var errorMessage = $"An application error occurred. Please contact the adminstrator with the following information:{Environment.NewLine}{Environment.NewLine}";
+
+ // Since we can't prevent the app from terminating, log this to the event log.
+ if (!EventLog.SourceExists("ThreadException"))
+ {
+ EventLog.CreateEventSource("ThreadException", "Application");
+ }
+
+ // Create an EventLog instance and assign its source.
+ var myLog = new EventLog { Source = "ThreadException" };
+ myLog.WriteEntry($"{errorMessage}{ex.Message}{Environment.NewLine}{Environment.NewLine}Stack Trace:{Environment.NewLine}{ex.StackTrace}");
+ }
+ catch (Exception)
+ {
+ try
+ {
+ MessageBox.Show("Fatal Non-UI Error. Could not write the error to the event log. Reason: {exc.Message}", "Fatal Non-UI Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
+ }
+ finally
+ {
+ Application.Exit();
+ }
+ }
+ }
+
+ // Creates the error message and displays it.
+ private static DialogResult ShowThreadExceptionDialog(string title, Exception e)
+ {
+ var errorMsg = $"An application error occurred. Please contact the adminstrator with the following information:{Environment.NewLine}{Environment.NewLine}";
+ errorMsg = $"{errorMsg}{e.Message}{Environment.NewLine}{Environment.NewLine}Stack Trace:{Environment.NewLine}{e.StackTrace}";
+ return MessageBox.Show(errorMsg, title, MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Stop);
+ }
+ }
+}
\ No newline at end of file
diff --git a/installer/RePlaysTV-Installer/Options.cs b/installer/RePlaysTV-Installer/Options.cs
deleted file mode 100644
index d8809b2..0000000
--- a/installer/RePlaysTV-Installer/Options.cs
+++ /dev/null
@@ -1,87 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Data;
-using System.Diagnostics;
-using System.Drawing;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Forms;
-
-namespace RePlaysTV_Installer {
- public partial class Options : Form {
- public Options() {
- InitializeComponent();
- }
-
- private void Options_FormClosing(object sender, FormClosingEventArgs e) {
- this.Hide();
- e.Cancel = true;
- }
-
- private void Options_Load(object sender, EventArgs e) {
- playsSetupUrl.Text = (string)Installer.installSettings["playsSetupUrl"];
- ltcVersion.Text = (string)Installer.installSettings["ltcVersion"];
- cleanInstall.Checked = (bool)Installer.installSettings["cleanInstall"];
- deleteTemp.Checked = (bool)Installer.installSettings["deleteTemp"];
- ignoreChecksum.Checked = (bool)Installer.installSettings["ignoreChecksum"];
- }
-
- private void Button5_Click(object sender, EventArgs e) {
- Installer.installSettings = new Dictionary()
- {
- { "playsSetupUrl", playsSetupUrl.Text },
- { "ltcVersion", ltcVersion.Text },
- { "cleanInstall", cleanInstall.Checked },
- { "deleteTemp", deleteTemp.Checked },
- { "ignoreChecksum", ignoreChecksum.Checked },
- };
- }
-
- private void Button1_Click(object sender, EventArgs e) {
- Process.Start(Directory.GetCurrentDirectory());
- }
-
- private void Button2_Click(object sender, EventArgs e) {
- Process.Start(Environment.GetEnvironmentVariable("LocalAppData") + "\\Plays");
- }
-
- private void Button3_Click(object sender, EventArgs e) {
- Process.Start(Environment.GetEnvironmentVariable("LocalAppData") + "\\Plays-ltc");
- }
-
- private void Button4_Click(object sender, EventArgs e) {
- Process.Start(Environment.GetEnvironmentVariable("AppData") + "\\Plays");
- }
-
- private void Button6_Click(object sender, EventArgs e) {
- DialogResult dr1 = MessageBox.Show("Are you sure you want to uninstall Plays?",
- "RePlaysTV Installer", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Information);
- if (dr1 == DialogResult.Yes) {
- Process.Start("cmd.exe", "/C \"" + Environment.GetEnvironmentVariable("LocalAppData") + "\\Plays\\Update.exe\" --uninstall &&" +
- "rd /s /q \"" + Environment.GetEnvironmentVariable("LocalAppData") + "\\Plays\" &&" +
- "rd /s /q \"" + Environment.GetEnvironmentVariable("LocalAppData") + "\\Plays-ltc\"");
- }
- }
-
- private void Button8_Click(object sender, EventArgs e) {
- DialogResult dr1 = MessageBox.Show("Are you sure you want to uninstall the RePlays Installer?\n" +
- "This will not uninstall Plays or RePlays patched Plays.",
- "RePlaysTV Installer", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Information);
- if (dr1 == DialogResult.Yes) {
-
- }
- }
-
- private void Button7_Click(object sender, EventArgs e) {
- DialogResult dr1 = MessageBox.Show("Are you sure you want to uninstall Yarn?\n" +
- "This will uninstall Yarn from your local version of npm if it is installed globally.",
- "RePlaysTV Installer", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Information);
- if (dr1 == DialogResult.Yes) {
-
- }
- }
- }
-}
diff --git a/installer/RePlaysTV-Installer/Properties/AssemblyInfo.cs b/installer/RePlaysTV-Installer/Properties/AssemblyInfo.cs
index 12b34d5..091ac4a 100644
--- a/installer/RePlaysTV-Installer/Properties/AssemblyInfo.cs
+++ b/installer/RePlaysTV-Installer/Properties/AssemblyInfo.cs
@@ -1,7 +1,10 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
+#region
+
+using System.Reflection;
using System.Runtime.InteropServices;
+#endregion
+
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
@@ -32,5 +35,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: AssemblyVersion("3.0.4.0")]
+[assembly: AssemblyFileVersion("3.0.4.0")]
\ No newline at end of file
diff --git a/installer/RePlaysTV-Installer/Properties/Settings.settings b/installer/RePlaysTV-Installer/Properties/Settings.settings
index 3964565..e04fc63 100644
--- a/installer/RePlaysTV-Installer/Properties/Settings.settings
+++ b/installer/RePlaysTV-Installer/Properties/Settings.settings
@@ -1,7 +1,8 @@
+
-
+
\ No newline at end of file
diff --git a/installer/RePlaysTV-Installer/RePlaysTV-Installer.csproj b/installer/RePlaysTV-Installer/RePlaysTV-Installer.csproj
index 9fdbd4d..6d201ec 100644
--- a/installer/RePlaysTV-Installer/RePlaysTV-Installer.csproj
+++ b/installer/RePlaysTV-Installer/RePlaysTV-Installer.csproj
@@ -48,25 +48,29 @@
-
+
+
+
+
+
Form
-
+
Main.cs
-
+
Form
-
+
Options.cs
-
+
-
+
Main.cs
-
+
Options.cs
@@ -90,5 +94,6 @@
True
+
\ No newline at end of file
diff --git a/installer/RePlaysTV-Installer/Settings/InstallerSetting.cs b/installer/RePlaysTV-Installer/Settings/InstallerSetting.cs
new file mode 100644
index 0000000..9c4ad89
--- /dev/null
+++ b/installer/RePlaysTV-Installer/Settings/InstallerSetting.cs
@@ -0,0 +1,15 @@
+namespace RePlaysTV_Installer.Settings
+{
+ internal enum InstallerSetting
+ {
+ PlaysSetupUrl,
+ CorrectPlaysSetupHash,
+ LtcVersion,
+ CleanInstall,
+ DeleteTemp,
+ IgnoreChecksum,
+ RePlaysDirectory,
+ Version,
+ PlaySetupExecutableSizeInBytes
+ }
+}
\ No newline at end of file
diff --git a/installer/RePlaysTV-Installer/Settings/InstallerSettings.cs b/installer/RePlaysTV-Installer/Settings/InstallerSettings.cs
new file mode 100644
index 0000000..815d8ad
--- /dev/null
+++ b/installer/RePlaysTV-Installer/Settings/InstallerSettings.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+
+namespace RePlaysTV_Installer.Settings
+{
+ internal static class InstallerSettings
+ {
+ private static readonly Dictionary InstallSettings =
+ new Dictionary
+ {
+ {
+ InstallerSetting.PlaysSetupUrl,
+ "https://web.archive.org/web/20191212211927if_/https://app-updates.plays.tv/builds/PlaysSetup.exe"
+ },
+ {InstallerSetting.LtcVersion, "0.54.7"},
+ {InstallerSetting.CleanInstall, true},
+ {InstallerSetting.DeleteTemp, false},
+ {InstallerSetting.IgnoreChecksum, false},
+ {InstallerSetting.Version, ""},
+ {InstallerSetting.RePlaysDirectory, $"{Environment.GetEnvironmentVariable("LocalAppData")}\\RePlays"},
+ {InstallerSetting.CorrectPlaysSetupHash, "3a7cea84d50ad2c31a79e66f5c3f3b8d"},
+ {InstallerSetting.PlaySetupExecutableSizeInBytes, 145310344M}
+ };
+
+ public static T GetInstallerSetting(string settingIdentifier)
+ {
+ if (string.IsNullOrWhiteSpace(settingIdentifier) ||
+ !Enum.TryParse(settingIdentifier, out var installerSetting))
+ throw new ArgumentNullException(nameof(settingIdentifier));
+
+ return GetInstallerSetting(installerSetting);
+ }
+
+ public static T GetInstallerSetting(InstallerSetting installerSetting)
+ {
+ return (T) (InstallSettings.TryGetValue(installerSetting, out var result) ? result : null);
+ }
+
+ public static bool RemoveInstallerSetting(string settingIdentifier)
+ {
+ if (string.IsNullOrWhiteSpace(settingIdentifier) ||
+ !Enum.TryParse(settingIdentifier, out var installerSetting))
+ throw new ArgumentNullException(nameof(settingIdentifier));
+
+ return RemoveInstallerSetting(installerSetting);
+ }
+
+ public static bool RemoveInstallerSetting(InstallerSetting installerSetting)
+ {
+ return InstallSettings.Remove(installerSetting);
+ }
+
+ public static void SetInstallerSetting(string settingIdentifier, object value)
+ {
+ if (string.IsNullOrWhiteSpace(settingIdentifier)
+ || value == null
+ || !Enum.TryParse(settingIdentifier, out var installerSetting))
+ throw new ArgumentNullException(nameof(settingIdentifier));
+
+ SetInstallerSetting(installerSetting, value);
+ }
+
+ public static void SetInstallerSetting(InstallerSetting installerSetting, object value)
+ {
+ if (value == null || InstallSettings == null ||
+ GetInstallerSetting