diff --git a/LANCommander.Playnite.Extension/InstallController.cs b/LANCommander.Playnite.Extension/InstallController.cs
index 7c7bb86..c762c86 100644
--- a/LANCommander.Playnite.Extension/InstallController.cs
+++ b/LANCommander.Playnite.Extension/InstallController.cs
@@ -1,11 +1,13 @@
using LANCommander.SDK;
using LANCommander.SDK.Helpers;
using LANCommander.SDK.Models;
+using LANCommander.SDK.PowerShell;
using Playnite.SDK;
using Playnite.SDK.Models;
using Playnite.SDK.Plugins;
using System;
using System.Diagnostics;
+using System.IO;
using System.Linq;
namespace LANCommander.PlaynitePlugin
@@ -116,6 +118,10 @@ namespace LANCommander.PlaynitePlugin
InstallDirectory = installDirectory,
};
+ RunInstallScript(installDirectory);
+ RunNameChangeScript(installDirectory);
+ RunKeyChangeScript(installDirectory);
+
InvokeOnInstalled(new GameInstalledEventArgs(installInfo));
}
else if (result.Canceled)
@@ -130,5 +136,76 @@ namespace LANCommander.PlaynitePlugin
else if (result.Error != null)
throw result.Error;
}
+
+ private int RunInstallScript(string installDirectory)
+ {
+ var manifest = ManifestHelper.Read(installDirectory);
+ var path = ScriptHelper.GetScriptFilePath(installDirectory, SDK.Enums.ScriptType.Install);
+
+ if (File.Exists(path))
+ {
+ var script = new PowerShellScript();
+
+ script.AddVariable("InstallDirectory", installDirectory);
+ script.AddVariable("GameManifest", manifest);
+ script.AddVariable("DefaultInstallDirectory", Plugin.Settings.InstallDirectory);
+ script.AddVariable("ServerAddress", Plugin.Settings.ServerAddress);
+
+ script.UseFile(ScriptHelper.GetScriptFilePath(installDirectory, SDK.Enums.ScriptType.Install));
+
+ return script.Execute();
+ }
+
+ return 0;
+ }
+
+ private int RunNameChangeScript(string installDirectory)
+ {
+ var manifest = ManifestHelper.Read(installDirectory);
+ var path = ScriptHelper.GetScriptFilePath(installDirectory, SDK.Enums.ScriptType.NameChange);
+
+ if (File.Exists(path))
+ {
+ var script = new PowerShellScript();
+
+ script.AddVariable("InstallDirectory", installDirectory);
+ script.AddVariable("GameManifest", manifest);
+ script.AddVariable("DefaultInstallDirectory", Plugin.Settings.InstallDirectory);
+ script.AddVariable("ServerAddress", Plugin.Settings.ServerAddress);
+ script.AddVariable("OldPlayerAlias", "");
+ script.AddVariable("NewPlayerAlias", Plugin.Settings.PlayerName);
+
+ script.UseFile(path);
+
+ return script.Execute();
+ }
+
+ return 0;
+ }
+
+ private int RunKeyChangeScript(string installDirectory)
+ {
+ var manifest = ManifestHelper.Read(installDirectory);
+ var path = ScriptHelper.GetScriptFilePath(installDirectory, SDK.Enums.ScriptType.KeyChange);
+
+ if (File.Exists(path))
+ {
+ var script = new PowerShellScript();
+
+ var key = Plugin.LANCommanderClient.GetAllocatedKey(manifest.Id);
+
+ script.AddVariable("InstallDirectory", installDirectory);
+ script.AddVariable("GameManifest", manifest);
+ script.AddVariable("DefaultInstallDirectory", Plugin.Settings.InstallDirectory);
+ script.AddVariable("ServerAddress", Plugin.Settings.ServerAddress);
+ script.AddVariable("AllocatedKey", key);
+
+ script.UseFile(path);
+
+ return script.Execute();
+ }
+
+ return 0;
+ }
}
}
diff --git a/LANCommander.Playnite.Extension/LANCommander.PlaynitePlugin.csproj b/LANCommander.Playnite.Extension/LANCommander.PlaynitePlugin.csproj
index 38dff6c..597df0b 100644
--- a/LANCommander.Playnite.Extension/LANCommander.PlaynitePlugin.csproj
+++ b/LANCommander.Playnite.Extension/LANCommander.PlaynitePlugin.csproj
@@ -12,6 +12,7 @@
v4.6.2
512
true
+ true
true
@@ -140,6 +141,10 @@
+
+ {807943bf-0c7d-4ed3-8393-cfee64e3138c}
+ LANCommander.PowerShell
+
{4c2a71fd-a30b-4d62-888a-4ef843d8e506}
LANCommander.SDK
diff --git a/LANCommander.Playnite.Extension/LANCommanderLibraryPlugin.cs b/LANCommander.Playnite.Extension/LANCommanderLibraryPlugin.cs
index e118e19..21926a0 100644
--- a/LANCommander.Playnite.Extension/LANCommanderLibraryPlugin.cs
+++ b/LANCommander.Playnite.Extension/LANCommanderLibraryPlugin.cs
@@ -1,4 +1,6 @@
using LANCommander.PlaynitePlugin.Extensions;
+using LANCommander.SDK.Helpers;
+using LANCommander.SDK.PowerShell;
using Playnite.SDK;
using Playnite.SDK.Events;
using Playnite.SDK.Models;
@@ -220,9 +222,9 @@ namespace LANCommander.PlaynitePlugin
if (args.Games.Count == 1 && args.Games.First().IsInstalled && !String.IsNullOrWhiteSpace(args.Games.First().InstallDirectory))
{
- var nameChangeScriptPath = LANCommander.SDK.PowerShellRuntime.GetScriptFilePath(args.Games.First().InstallDirectory, SDK.Enums.ScriptType.NameChange);
- var keyChangeScriptPath = LANCommander.SDK.PowerShellRuntime.GetScriptFilePath(args.Games.First().InstallDirectory, SDK.Enums.ScriptType.KeyChange);
- var installScriptPath = LANCommander.SDK.PowerShellRuntime.GetScriptFilePath(args.Games.First().InstallDirectory, SDK.Enums.ScriptType.Install);
+ var nameChangeScriptPath = ScriptHelper.GetScriptFilePath(args.Games.First().InstallDirectory, SDK.Enums.ScriptType.NameChange);
+ var keyChangeScriptPath = ScriptHelper.GetScriptFilePath(args.Games.First().InstallDirectory, SDK.Enums.ScriptType.KeyChange);
+ var installScriptPath = ScriptHelper.GetScriptFilePath(args.Games.First().InstallDirectory, SDK.Enums.ScriptType.Install);
if (File.Exists(nameChangeScriptPath))
{
@@ -241,7 +243,8 @@ namespace LANCommander.PlaynitePlugin
{
var game = nameChangeArgs.Games.First();
- LANCommander.SDK.PowerShellRuntime.RunScript(game.InstallDirectory, SDK.Enums.ScriptType.NameChange, $@"""{result.SelectedString}"" ""{oldName}""");
+ RunNameChangeScript(game.InstallDirectory, oldName, result.SelectedString);
+
LANCommanderClient.ChangeAlias(result.SelectedString);
}
}
@@ -267,7 +270,7 @@ namespace LANCommander.PlaynitePlugin
if (String.IsNullOrEmpty(newKey))
PlayniteApi.Dialogs.ShowErrorMessage("There are no more keys available on the server.", "No Keys Available");
else
- LANCommander.SDK.PowerShellRuntime.RunScript(keyChangeArgs.Games.First().InstallDirectory, SDK.Enums.ScriptType.KeyChange, $@"""{newKey}""");
+ RunKeyChangeScript(keyChangeArgs.Games.First().InstallDirectory, newKey);
}
else
{
@@ -289,13 +292,9 @@ namespace LANCommander.PlaynitePlugin
Guid gameId;
if (Guid.TryParse(installArgs.Games.First().GameId, out gameId))
- {
- LANCommander.SDK.PowerShellRuntime.RunScript(installArgs.Games.First().InstallDirectory, SDK.Enums.ScriptType.Install);
- }
+ RunInstallScript(installArgs.Games.First().InstallDirectory);
else
- {
PlayniteApi.Dialogs.ShowErrorMessage("This game could not be found on the server. Your game may be corrupted.");
- }
}
};
}
@@ -391,6 +390,8 @@ namespace LANCommander.PlaynitePlugin
}
else
{
+ var oldName = Settings.PlayerName;
+
Settings.PlayerName = result.SelectedString;
Logger.Trace($"New player name of \"{Settings.PlayerName}\" has been set!");
@@ -404,7 +405,17 @@ namespace LANCommander.PlaynitePlugin
Logger.Trace($"Running name change scripts across {games.Count} installed game(s)");
- LANCommander.SDK.PowerShellRuntime.RunScripts(games.Select(g => g.InstallDirectory), SDK.Enums.ScriptType.NameChange, Settings.PlayerName);
+ foreach (var game in games)
+ {
+ var script = new PowerShellScript();
+
+ script.AddVariable("OldName", oldName);
+ script.AddVariable("NewName", Settings.PlayerName);
+
+ script.UseFile(ScriptHelper.GetScriptFilePath(game.InstallDirectory, SDK.Enums.ScriptType.NameChange));
+
+ script.Execute();
+ }
}
}
else
@@ -523,5 +534,77 @@ namespace LANCommander.PlaynitePlugin
PlayniteApi.Database.Games.Update(game);
}
+
+ private int RunInstallScript(string installDirectory)
+ {
+ var manifest = ManifestHelper.Read(installDirectory);
+ var path = ScriptHelper.GetScriptFilePath(installDirectory, SDK.Enums.ScriptType.Install);
+
+ if (File.Exists(path))
+ {
+ var script = new PowerShellScript();
+
+ script.AddVariable("InstallDirectory", installDirectory);
+ script.AddVariable("GameManifest", manifest);
+ script.AddVariable("DefaultInstallDirectory", Settings.InstallDirectory);
+ script.AddVariable("ServerAddress", Settings.ServerAddress);
+
+ script.UseFile(path);
+
+ return script.Execute();
+ }
+
+ return 0;
+ }
+
+ private int RunNameChangeScript(string installDirectory, string oldPlayerAlias, string newPlayerAlias)
+ {
+ var manifest = ManifestHelper.Read(installDirectory);
+ var path = ScriptHelper.GetScriptFilePath(installDirectory, SDK.Enums.ScriptType.NameChange);
+
+ if (File.Exists(path))
+ {
+ var script = new PowerShellScript();
+
+ script.AddVariable("InstallDirectory", installDirectory);
+ script.AddVariable("GameManifest", manifest);
+ script.AddVariable("DefaultInstallDirectory", Settings.InstallDirectory);
+ script.AddVariable("ServerAddress", Settings.ServerAddress);
+ script.AddVariable("OldPlayerAlias", oldPlayerAlias);
+ script.AddVariable("NewPlayerAlias", newPlayerAlias);
+
+ script.UseFile(path);
+
+ return script.Execute();
+ }
+
+ return 0;
+ }
+
+ private int RunKeyChangeScript(string installDirectory, string key = "")
+ {
+ var manifest = ManifestHelper.Read(installDirectory);
+ var path = ScriptHelper.GetScriptFilePath(installDirectory, SDK.Enums.ScriptType.KeyChange);
+
+ if (File.Exists(path))
+ {
+ var script = new PowerShellScript();
+
+ if (String.IsNullOrEmpty(key))
+ key = LANCommanderClient.GetAllocatedKey(manifest.Id);
+
+ script.AddVariable("InstallDirectory", installDirectory);
+ script.AddVariable("GameManifest", manifest);
+ script.AddVariable("DefaultInstallDirectory", Settings.InstallDirectory);
+ script.AddVariable("ServerAddress", Settings.ServerAddress);
+ script.AddVariable("AllocatedKey", key);
+
+ script.UseFile(path);
+
+ return script.Execute();
+ }
+
+ return 0;
+ }
}
}
diff --git a/LANCommander.Playnite.Extension/UninstallController.cs b/LANCommander.Playnite.Extension/UninstallController.cs
index eb23735..df9788f 100644
--- a/LANCommander.Playnite.Extension/UninstallController.cs
+++ b/LANCommander.Playnite.Extension/UninstallController.cs
@@ -1,4 +1,6 @@
using LANCommander.SDK.Enums;
+using LANCommander.SDK.Helpers;
+using LANCommander.SDK.PowerShell;
using Playnite.SDK;
using Playnite.SDK.Models;
using Playnite.SDK.Plugins;
@@ -25,11 +27,35 @@ namespace LANCommander.PlaynitePlugin
{
var gameManager = new LANCommander.SDK.GameManager(Plugin.LANCommanderClient, Plugin.Settings.InstallDirectory);
+ try
+ {
+ var scriptPath = ScriptHelper.GetScriptFilePath(Game.InstallDirectory, SDK.Enums.ScriptType.Uninstall);
+
+ if (!String.IsNullOrEmpty(scriptPath) && File.Exists(scriptPath))
+ {
+ var manifest = ManifestHelper.Read(Game.InstallDirectory);
+ var script = new PowerShellScript();
+
+ script.AddVariable("InstallDirectory", Game.InstallDirectory);
+ script.AddVariable("GameManifest", manifest);
+ script.AddVariable("DefaultInstallDirectory", Plugin.Settings.InstallDirectory);
+ script.AddVariable("ServerAddress", Plugin.Settings.ServerAddress);
+
+ script.UseFile(scriptPath);
+
+ script.Execute();
+ }
+ }
+ catch (Exception ex)
+ {
+ Logger.Error(ex, "There was an error running the uninstall script");
+ }
+
gameManager.Uninstall(Game.InstallDirectory);
}
catch (Exception ex)
{
-
+ Logger.Error(ex, "There was an error uninstalling the game");
}
InvokeOnUninstalled(new GameUninstalledEventArgs());
diff --git a/LANCommander.PowerShell.Tests/Cmdlets.cs b/LANCommander.PowerShell.Tests/Cmdlets.cs
new file mode 100644
index 0000000..b56e654
--- /dev/null
+++ b/LANCommander.PowerShell.Tests/Cmdlets.cs
@@ -0,0 +1,64 @@
+using LANCommander.PowerShell.Cmdlets;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.Linq;
+
+namespace LANCommander.PowerShell.Tests
+{
+ [TestClass]
+ public class CmdletTests
+ {
+ [TestMethod]
+ public void ConvertToSerializedBase64ShouldBeDeserializable()
+ {
+ var testPhrase = "Hello world! This should be deserializable back to its original form.";
+
+ var encodingCmdlet = new ConvertToSerializedBase64Cmdlet()
+ {
+ Input = testPhrase
+ };
+
+ var encodingResults = encodingCmdlet.Invoke().OfType().ToList();
+
+ Assert.AreEqual(1, encodingResults.Count);
+
+ var decodingCmdlet = new ConvertFromSerializedBase64Cmdlet()
+ {
+ Input = encodingResults.First()
+ };
+
+ var decodingResults = decodingCmdlet.Invoke().OfType().ToList();
+
+ Assert.AreEqual(1, encodingResults.Count);
+ Assert.AreEqual(testPhrase, decodingResults.First());
+ }
+
+ [TestMethod]
+ [DataRow(640, 480, 640, 360, 16, 9)]
+ [DataRow(1024, 768, 1024, 576, 16, 9)]
+ [DataRow(1600, 1200, 1600, 900, 16, 9)]
+ [DataRow(1920, 1080, 1440, 1080, 4, 3)]
+ [DataRow(1366, 1024, 1024, 768, 4, 3)]
+ [DataRow(854, 480, 640, 480, 4, 3)]
+ public void ConvertAspectRatioShouldReturnCorrectBounds(int x1, int y1, int x2, int y2, int ratioX, int ratioY)
+ {
+ var aspectRatio = (double)ratioX / (double)ratioY;
+
+ var cmdlet = new ConvertAspectRatioCmdlet()
+ {
+ AspectRatio = aspectRatio,
+ Width = x1,
+ Height = y1
+ };
+
+ var output = cmdlet.Invoke().OfType().ToList();
+
+ Assert.AreEqual(1, output.Count);
+
+ var bounds = output.First();
+
+ Assert.AreEqual(x2, bounds.Width);
+ Assert.AreEqual(y2, bounds.Height);
+ }
+ }
+}
diff --git a/LANCommander.PowerShell.Tests/LANCommander.PowerShell.Tests.csproj b/LANCommander.PowerShell.Tests/LANCommander.PowerShell.Tests.csproj
new file mode 100644
index 0000000..af5df94
--- /dev/null
+++ b/LANCommander.PowerShell.Tests/LANCommander.PowerShell.Tests.csproj
@@ -0,0 +1,75 @@
+
+
+
+
+
+ Debug
+ AnyCPU
+ {D7069A13-F0AA-4CBF-9013-4276F130A6DD}
+ Library
+ Properties
+ LANCommander.PowerShell.Tests
+ LANCommander.PowerShell.Tests
+ v4.6.2
+ 512
+ {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 15.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+ $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
+ False
+ UnitTest
+
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\MSTest.TestFramework.2.2.10\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll
+
+
+ ..\packages\MSTest.TestFramework.2.2.10\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {807943bf-0c7d-4ed3-8393-cfee64e3138c}
+ LANCommander.PowerShell
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LANCommander.PowerShell.Tests/Properties/AssemblyInfo.cs b/LANCommander.PowerShell.Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..82c8b70
--- /dev/null
+++ b/LANCommander.PowerShell.Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,20 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("LANCommander.PowerShell.Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("LANCommander.PowerShell.Tests")]
+[assembly: AssemblyCopyright("Copyright © 2023")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+
+[assembly: Guid("d7069a13-f0aa-4cbf-9013-4276f130a6dd")]
+
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/LANCommander.PowerShell.Tests/packages.config b/LANCommander.PowerShell.Tests/packages.config
new file mode 100644
index 0000000..e47cc4d
--- /dev/null
+++ b/LANCommander.PowerShell.Tests/packages.config
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/LANCommander.PowerShell/Cmdlets/Convert-AspectRatio.cs b/LANCommander.PowerShell/Cmdlets/Convert-AspectRatio.cs
new file mode 100644
index 0000000..a31949a
--- /dev/null
+++ b/LANCommander.PowerShell/Cmdlets/Convert-AspectRatio.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Management.Automation;
+
+namespace LANCommander.PowerShell.Cmdlets
+{
+ public class DisplayResolution
+ {
+ public int Width { get; set; }
+ public int Height { get; set; }
+ }
+
+ [Cmdlet(VerbsData.Convert, "AspectRatio")]
+ [OutputType(typeof(string))]
+ public class ConvertAspectRatioCmdlet : Cmdlet
+ {
+ [Parameter(Mandatory = true, Position = 0)]
+ public int Width { get; set; }
+
+ [Parameter(Mandatory = true, Position = 1)]
+ public int Height { get; set; }
+
+ [Parameter(Mandatory = true, Position = 2)]
+ public double AspectRatio { get; set; }
+
+ protected override void ProcessRecord()
+ {
+ var resolution = new DisplayResolution();
+
+ // Display is wider, pillar box
+ if ((Width / Height) < AspectRatio)
+ {
+ resolution.Width = (int)Math.Ceiling(Height * AspectRatio);
+ resolution.Height = Height;
+ }
+ // Letterbox
+ else
+ {
+ resolution.Width = Width;
+ resolution.Height = (int)Math.Ceiling(Width * (1 / AspectRatio));
+ }
+
+ WriteObject(resolution);
+ }
+ }
+}
diff --git a/LANCommander.PowerShell/Cmdlets/ConvertFrom-SerializedBase64.cs b/LANCommander.PowerShell/Cmdlets/ConvertFrom-SerializedBase64.cs
new file mode 100644
index 0000000..d600b38
--- /dev/null
+++ b/LANCommander.PowerShell/Cmdlets/ConvertFrom-SerializedBase64.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Management.Automation;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace LANCommander.PowerShell.Cmdlets
+{
+ [Cmdlet(VerbsData.ConvertFrom, "SerializedBase64")]
+ [OutputType(typeof(object))]
+ public class ConvertFromSerializedBase64Cmdlet : Cmdlet
+ {
+ [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)]
+ public string Input { get; set; }
+
+ protected override void ProcessRecord()
+ {
+ var xml = Encoding.UTF8.GetString(Convert.FromBase64String(Input));
+
+ WriteObject(PSSerializer.Deserialize(xml));
+ }
+ }
+}
diff --git a/LANCommander.PowerShell/Cmdlets/ConvertTo-SerializedBase64.cs b/LANCommander.PowerShell/Cmdlets/ConvertTo-SerializedBase64.cs
new file mode 100644
index 0000000..fe92654
--- /dev/null
+++ b/LANCommander.PowerShell/Cmdlets/ConvertTo-SerializedBase64.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Management.Automation;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace LANCommander.PowerShell.Cmdlets
+{
+ [Cmdlet(VerbsData.ConvertTo, "SerializedBase64")]
+ [OutputType(typeof(object))]
+ public class ConvertToSerializedBase64Cmdlet : Cmdlet
+ {
+ [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)]
+ public object Input { get; set; }
+
+ protected override void ProcessRecord()
+ {
+ var output = Convert.ToBase64String(Encoding.UTF8.GetBytes(PSSerializer.Serialize(Input)));
+
+ WriteObject(output);
+ }
+ }
+}
diff --git a/LANCommander.PowerShell/Cmdlets/ConvertTo-StringBytes.cs b/LANCommander.PowerShell/Cmdlets/ConvertTo-StringBytes.cs
new file mode 100644
index 0000000..dced3b9
--- /dev/null
+++ b/LANCommander.PowerShell/Cmdlets/ConvertTo-StringBytes.cs
@@ -0,0 +1,40 @@
+using LANCommander.SDK;
+using LANCommander.SDK.Helpers;
+using System.Management.Automation;
+
+namespace LANCommander.PowerShell.Cmdlets
+{
+ [Cmdlet(VerbsData.ConvertTo, "StringBytes")]
+ [OutputType(typeof(byte[]))]
+ public class ConvertToStringBytesCmdlet : Cmdlet
+ {
+ [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)]
+ public string Input { get; set; }
+
+ [Parameter]
+ public bool Utf16 { get; set; } = false;
+
+ [Parameter]
+ public bool BigEndian { get; set; } = false;
+
+ [Parameter]
+ public int MaxLength { get; set; } = 0;
+
+ protected override void ProcessRecord()
+ {
+ byte[] output;
+
+ if (MaxLength > 0 && Input.Length > MaxLength)
+ Input = Input.Substring(0, MaxLength);
+
+ if (Utf16 && BigEndian)
+ output = System.Text.Encoding.BigEndianUnicode.GetBytes(Input);
+ else if (Utf16)
+ output = System.Text.Encoding.Unicode.GetBytes(Input);
+ else
+ output = System.Text.Encoding.ASCII.GetBytes(Input);
+
+ WriteObject(output);
+ }
+ }
+}
diff --git a/LANCommander.PowerShell/Cmdlets/Edit-PatchBinary.cs b/LANCommander.PowerShell/Cmdlets/Edit-PatchBinary.cs
new file mode 100644
index 0000000..95e2e46
--- /dev/null
+++ b/LANCommander.PowerShell/Cmdlets/Edit-PatchBinary.cs
@@ -0,0 +1,30 @@
+using System;
+using System.IO;
+using System.Management.Automation;
+
+namespace LANCommander.PowerShell.Cmdlets
+{
+ [Cmdlet(VerbsData.Edit, "PatchBinary")]
+ [OutputType(typeof(string))]
+ public class EditPatchBinaryCmdlet : Cmdlet
+ {
+ [Parameter(Mandatory = true, Position = 0)]
+ public long Offset { get; set; }
+
+ [Parameter(Mandatory = true, Position = 1)]
+ public byte[] Data { get; set; }
+
+ [Parameter(Mandatory = true, Position = 2)]
+ public string FilePath { get; set; }
+
+ protected override void ProcessRecord()
+ {
+ using (var writer = File.OpenWrite(FilePath))
+ {
+ writer.Seek(Offset, SeekOrigin.Begin);
+
+ writer.Write(Data, 0, Data.Length);
+ }
+ }
+ }
+}
diff --git a/LANCommander.PowerShell/Cmdlets/Get-GameManifest.cs b/LANCommander.PowerShell/Cmdlets/Get-GameManifest.cs
new file mode 100644
index 0000000..d11ea2b
--- /dev/null
+++ b/LANCommander.PowerShell/Cmdlets/Get-GameManifest.cs
@@ -0,0 +1,19 @@
+using LANCommander.SDK;
+using LANCommander.SDK.Helpers;
+using System.Management.Automation;
+
+namespace LANCommander.PowerShell.Cmdlets
+{
+ [Cmdlet(VerbsCommon.Get, "GameManifest")]
+ [OutputType(typeof(GameManifest))]
+ public class GetGameManifestCmdlet : Cmdlet
+ {
+ [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)]
+ public string Path { get; set; }
+
+ protected override void ProcessRecord()
+ {
+ WriteObject(ManifestHelper.Read(Path));
+ }
+ }
+}
diff --git a/LANCommander.PowerShell/Cmdlets/Get-PrimaryDisplay.cs b/LANCommander.PowerShell/Cmdlets/Get-PrimaryDisplay.cs
new file mode 100644
index 0000000..93c838e
--- /dev/null
+++ b/LANCommander.PowerShell/Cmdlets/Get-PrimaryDisplay.cs
@@ -0,0 +1,18 @@
+using System.Linq;
+using System.Management.Automation;
+using System.Windows.Forms;
+
+namespace LANCommander.PowerShell.Cmdlets
+{
+ [Cmdlet(VerbsCommon.Get, "PrimaryDisplay")]
+ [OutputType(typeof(string))]
+ public class GetPrimaryDisplayCmdlet : Cmdlet
+ {
+ protected override void ProcessRecord()
+ {
+ var screens = Screen.AllScreens;
+
+ WriteObject(screens.First(s => s.Primary));
+ }
+ }
+}
diff --git a/LANCommander.PowerShell/Cmdlets/Install-Game.cs b/LANCommander.PowerShell/Cmdlets/Install-Game.cs
new file mode 100644
index 0000000..c24b5ed
--- /dev/null
+++ b/LANCommander.PowerShell/Cmdlets/Install-Game.cs
@@ -0,0 +1,132 @@
+using LANCommander.SDK;
+using LANCommander.SDK.Helpers;
+using LANCommander.SDK.PowerShell;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Management.Automation;
+using System.Windows.Forms;
+
+namespace LANCommander.PowerShell.Cmdlets
+{
+ [Cmdlet(VerbsLifecycle.Install, "Game")]
+ [OutputType(typeof(string))]
+ public class InstallGameCmdlet : Cmdlet
+ {
+ [Parameter(Mandatory = true)]
+ public Client Client { get; set; }
+
+ [Parameter(Mandatory = true)]
+ public Guid Id { get; set; }
+
+ [Parameter(Mandatory = false)]
+ public string InstallDirectory { get; set; } = "C:\\Games";
+
+ protected override void ProcessRecord()
+ {
+ var gameManager = new GameManager(Client, InstallDirectory);
+ var game = Client.GetGame(Id);
+
+ var progress = new ProgressRecord(1, $"Installing {game.Title}", "Progress:");
+
+ Stopwatch stopwatch = new Stopwatch();
+ stopwatch.Start();
+
+ gameManager.OnArchiveExtractionProgress += (long position, long length) =>
+ {
+ // Only update a max of every 500ms
+ if (stopwatch.ElapsedMilliseconds > 500)
+ {
+ progress.PercentComplete = (int)Math.Ceiling((position / (decimal)length) * 100);
+
+ WriteProgress(progress);
+
+ stopwatch.Restart();
+ }
+ };
+
+ var installDirectory = gameManager.Install(Id);
+
+ stopwatch.Stop();
+
+ RunInstallScript(installDirectory);
+ RunNameChangeScript(installDirectory);
+ RunKeyChangeScript(installDirectory);
+
+ WriteObject(installDirectory);
+ }
+
+ private int RunInstallScript(string installDirectory)
+ {
+ var manifest = ManifestHelper.Read(installDirectory);
+ var path = ScriptHelper.GetScriptFilePath(installDirectory, SDK.Enums.ScriptType.Install);
+
+ if (File.Exists(path))
+ {
+ var script = new PowerShellScript();
+
+ script.AddVariable("InstallDirectory", installDirectory);
+ script.AddVariable("GameManifest", manifest);
+ script.AddVariable("DefaultInstallDirectory", InstallDirectory);
+ script.AddVariable("ServerAddress", Client.BaseUrl);
+
+ script.UseFile(ScriptHelper.GetScriptFilePath(installDirectory, SDK.Enums.ScriptType.Install));
+
+ return script.Execute();
+ }
+
+ return 0;
+ }
+
+ private int RunNameChangeScript(string installDirectory)
+ {
+ var user = Client.GetProfile();
+ var manifest = ManifestHelper.Read(installDirectory);
+ var path = ScriptHelper.GetScriptFilePath(installDirectory, SDK.Enums.ScriptType.NameChange);
+
+ if (File.Exists(path))
+ {
+ var script = new PowerShellScript();
+
+ script.AddVariable("InstallDirectory", installDirectory);
+ script.AddVariable("GameManifest", manifest);
+ script.AddVariable("DefaultInstallDirectory", InstallDirectory);
+ script.AddVariable("ServerAddress", Client.BaseUrl);
+ script.AddVariable("OldPlayerAlias", "");
+ script.AddVariable("NewPlayerAlias", user.UserName);
+
+ script.UseFile(path);
+
+ return script.Execute();
+ }
+
+ return 0;
+ }
+
+ private int RunKeyChangeScript(string installDirectory)
+ {
+ var manifest = ManifestHelper.Read(installDirectory);
+ var path = ScriptHelper.GetScriptFilePath(installDirectory, SDK.Enums.ScriptType.KeyChange);
+
+ if (File.Exists(path))
+ {
+ var script = new PowerShellScript();
+
+ var key = Client.GetAllocatedKey(manifest.Id);
+
+ script.AddVariable("InstallDirectory", installDirectory);
+ script.AddVariable("GameManifest", manifest);
+ script.AddVariable("DefaultInstallDirectory", InstallDirectory);
+ script.AddVariable("ServerAddress", Client.BaseUrl);
+ script.AddVariable("AllocatedKey", key);
+
+ script.UseFile(path);
+
+ return script.Execute();
+ }
+
+ return 0;
+ }
+ }
+}
diff --git a/LANCommander.PowerShell/Cmdlets/Uninstall-Game.cs b/LANCommander.PowerShell/Cmdlets/Uninstall-Game.cs
new file mode 100644
index 0000000..27b3dc4
--- /dev/null
+++ b/LANCommander.PowerShell/Cmdlets/Uninstall-Game.cs
@@ -0,0 +1,42 @@
+using LANCommander.SDK;
+using LANCommander.SDK.Helpers;
+using LANCommander.SDK.PowerShell;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Management.Automation;
+using System.Windows.Forms;
+
+namespace LANCommander.PowerShell.Cmdlets
+{
+ [Cmdlet(VerbsLifecycle.Uninstall, "Game")]
+ [OutputType(typeof(string))]
+ public class UninstallGameCmdlet : Cmdlet
+ {
+ [Parameter(Mandatory = true)]
+ public string InstallDirectory { get; set; }
+
+ protected override void ProcessRecord()
+ {
+ var scriptPath = ScriptHelper.GetScriptFilePath(InstallDirectory, SDK.Enums.ScriptType.Uninstall);
+
+ if (!String.IsNullOrEmpty(scriptPath) && File.Exists(scriptPath))
+ {
+ var manifest = ManifestHelper.Read(InstallDirectory);
+ var script = new PowerShellScript();
+
+ script.AddVariable("InstallDirectory", InstallDirectory);
+ script.AddVariable("GameManifest", manifest);
+
+ script.UseFile(scriptPath);
+
+ script.Execute();
+ }
+
+ var gameManager = new GameManager(null, InstallDirectory);
+
+ gameManager.Uninstall(InstallDirectory);
+ }
+ }
+}
diff --git a/LANCommander.PowerShell/Cmdlets/Write-GameManifest.cs b/LANCommander.PowerShell/Cmdlets/Write-GameManifest.cs
new file mode 100644
index 0000000..28ff74d
--- /dev/null
+++ b/LANCommander.PowerShell/Cmdlets/Write-GameManifest.cs
@@ -0,0 +1,24 @@
+using LANCommander.SDK;
+using LANCommander.SDK.Helpers;
+using System.Management.Automation;
+
+namespace LANCommander.PowerShell.Cmdlets
+{
+ [Cmdlet(VerbsCommunications.Write, "GameManifest")]
+ [OutputType(typeof(string))]
+ public class WriteGameManifestCmdlet : Cmdlet
+ {
+ [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)]
+ public string Path { get; set; }
+
+ [Parameter(Mandatory = true, Position = 1, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)]
+ public GameManifest Manifest { get; set; }
+
+ protected override void ProcessRecord()
+ {
+ var destination = ManifestHelper.Write(Manifest, Path);
+
+ WriteObject(destination);
+ }
+ }
+}
diff --git a/LANCommander.PowerShell/Cmdlets/Write-ReplaceContentInFile.cs b/LANCommander.PowerShell/Cmdlets/Write-ReplaceContentInFile.cs
new file mode 100644
index 0000000..3a96f72
--- /dev/null
+++ b/LANCommander.PowerShell/Cmdlets/Write-ReplaceContentInFile.cs
@@ -0,0 +1,31 @@
+using System.IO;
+using System.Management.Automation;
+using System.Text.RegularExpressions;
+
+namespace LANCommander.PowerShell.Cmdlets
+{
+
+ [Cmdlet(VerbsCommunications.Write, "ReplaceContentInFile")]
+ [OutputType(typeof(string))]
+ public class ReplaceContentInFileCmdlet : Cmdlet
+ {
+ [Parameter(Mandatory = true, Position = 0)]
+ public string Pattern { get; set; }
+
+ [Parameter(Mandatory = true, Position = 1)]
+ public string Substitution { get; set; }
+
+ [Parameter(Mandatory = true, Position = 2)]
+ public string FilePath { get; set; }
+
+ protected override void ProcessRecord()
+ {
+ var contents = File.ReadAllText(FilePath);
+ var regex = new Regex(Pattern, RegexOptions.Multiline);
+
+ var result = regex.Replace(contents, Substitution);
+
+ WriteObject(result);
+ }
+ }
+}
diff --git a/LANCommander.PowerShell/LANCommander.PowerShell.csproj b/LANCommander.PowerShell/LANCommander.PowerShell.csproj
new file mode 100644
index 0000000..b59a2f6
--- /dev/null
+++ b/LANCommander.PowerShell/LANCommander.PowerShell.csproj
@@ -0,0 +1,75 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {807943BF-0C7D-4ED3-8393-CFEE64E3138C}
+ Library
+ Properties
+ LANCommander.PowerShell
+ LANCommander.PowerShell
+ v4.6.2
+ 512
+ true
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+ ..\packages\PowerShellStandard.Library.5.1.1\lib\net452\System.Management.Automation.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+ {4c2a71fd-a30b-4d62-888a-4ef843d8e506}
+ LANCommander.SDK
+
+
+
+
\ No newline at end of file
diff --git a/LANCommander.PowerShell/LANCommander.PowerShell.psd1 b/LANCommander.PowerShell/LANCommander.PowerShell.psd1
new file mode 100644
index 0000000..7237c50
Binary files /dev/null and b/LANCommander.PowerShell/LANCommander.PowerShell.psd1 differ
diff --git a/LANCommander.PowerShell/Properties/AssemblyInfo.cs b/LANCommander.PowerShell/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..87468b6
--- /dev/null
+++ b/LANCommander.PowerShell/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 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.
+[assembly: AssemblyTitle("LANCommander.PowerShell")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("LANCommander.PowerShell")]
+[assembly: AssemblyCopyright("Copyright © 2023")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("807943bf-0c7d-4ed3-8393-cfee64e3138c")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// 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")]
diff --git a/LANCommander.PowerShell/packages.config b/LANCommander.PowerShell/packages.config
new file mode 100644
index 0000000..411c02f
--- /dev/null
+++ b/LANCommander.PowerShell/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/LANCommander.SDK/Client.cs b/LANCommander.SDK/Client.cs
index d7db648..4f8bdaa 100644
--- a/LANCommander.SDK/Client.cs
+++ b/LANCommander.SDK/Client.cs
@@ -18,18 +18,24 @@ namespace LANCommander.SDK
private readonly ILogger Logger;
private readonly RestClient ApiClient;
- private AuthToken Token;
+ private AuthToken Token;
+
+ public string BaseUrl;
public Client(string baseUrl)
{
- if (!String.IsNullOrWhiteSpace(baseUrl))
- ApiClient = new RestClient(baseUrl);
+ BaseUrl = baseUrl;
+
+ if (!String.IsNullOrWhiteSpace(BaseUrl))
+ ApiClient = new RestClient(BaseUrl);
}
public Client(string baseUrl, ILogger logger)
{
- if (!String.IsNullOrWhiteSpace(baseUrl))
- ApiClient = new RestClient(baseUrl);
+ BaseUrl = baseUrl;
+
+ if (!String.IsNullOrWhiteSpace(BaseUrl))
+ ApiClient = new RestClient(BaseUrl);
Logger = logger;
}
diff --git a/LANCommander.SDK/GameManager.cs b/LANCommander.SDK/GameManager.cs
index 056b563..a9163cc 100644
--- a/LANCommander.SDK/GameManager.cs
+++ b/LANCommander.SDK/GameManager.cs
@@ -91,36 +91,11 @@ namespace LANCommander.SDK
ScriptHelper.SaveScript(game, ScriptType.NameChange);
ScriptHelper.SaveScript(game, ScriptType.KeyChange);
- try
- {
- PowerShellRuntime.RunScript(game, ScriptType.Install);
- PowerShellRuntime.RunScript(game, ScriptType.NameChange, /* Plugin.Settings.PlayerName */ "");
-
- var key = Client.GetAllocatedKey(game.Id);
-
- PowerShellRuntime.RunScript(game, ScriptType.KeyChange, $"\"{key}\"");
- }
- catch (Exception ex)
- {
- Logger?.LogError(ex, "Could not execute post-install scripts");
- }
-
return result.Directory;
}
public void Uninstall(string installDirectory)
{
- var manifest = ManifestHelper.Read(installDirectory);
-
- try
- {
- Logger?.LogTrace("Running uninstall script");
- PowerShellRuntime.RunScript(installDirectory, ScriptType.Uninstall);
- }
- catch (Exception ex)
- {
- Logger?.LogError(ex, "Error running uninstall script");
- }
Logger?.LogTrace("Attempting to delete the install directory");
diff --git a/LANCommander.SDK/GameSaveManager.cs b/LANCommander.SDK/GameSaveManager.cs
index a7ffc1b..caf53de 100644
--- a/LANCommander.SDK/GameSaveManager.cs
+++ b/LANCommander.SDK/GameSaveManager.cs
@@ -1,6 +1,7 @@
using LANCommander.SDK;
using LANCommander.SDK.Helpers;
using LANCommander.SDK.Models;
+using LANCommander.SDK.PowerShell;
using SharpCompress.Archives;
using SharpCompress.Archives.Zip;
using SharpCompress.Common;
@@ -126,7 +127,14 @@ namespace LANCommander.SDK
{
var registryImportFileContents = File.ReadAllText(registryImportFilePath);
- PowerShellRuntime.RunCommand($"regedit.exe /s \"{registryImportFilePath}\"", registryImportFileContents.Contains("HKEY_LOCAL_MACHINE"));
+ var script = new PowerShellScript();
+
+ script.UseInline($"regedit.exe /s \"{registryImportFilePath}\"");
+
+ if (registryImportFileContents.Contains("HKEY_LOCAL_MACHINE"))
+ script.RunAsAdmin();
+
+ script.Execute();
}
#endregion
@@ -199,7 +207,11 @@ namespace LANCommander.SDK
tempRegFiles.Add(tempRegFile);
}
- PowerShellRuntime.RunCommand(exportCommand.ToString());
+ var script = new PowerShellScript();
+
+ script.UseInline(exportCommand.ToString());
+
+ script.Execute();
var exportFile = new StringBuilder();
diff --git a/LANCommander.SDK/Helpers/ManifestHelper.cs b/LANCommander.SDK/Helpers/ManifestHelper.cs
index 4c09f16..9f87cef 100644
--- a/LANCommander.SDK/Helpers/ManifestHelper.cs
+++ b/LANCommander.SDK/Helpers/ManifestHelper.cs
@@ -30,7 +30,7 @@ namespace LANCommander.SDK.Helpers
return manifest;
}
- public static void Write(GameManifest manifest, string installDirectory)
+ public static string Write(GameManifest manifest, string installDirectory)
{
var destination = GetPath(installDirectory);
@@ -47,6 +47,8 @@ namespace LANCommander.SDK.Helpers
Logger?.LogTrace("Writing manifest file");
File.WriteAllText(destination, yaml);
+
+ return destination;
}
public static string GetPath(string installDirectory)
diff --git a/LANCommander.SDK/Helpers/ScriptHelper.cs b/LANCommander.SDK/Helpers/ScriptHelper.cs
index 3232e57..3fde3f8 100644
--- a/LANCommander.SDK/Helpers/ScriptHelper.cs
+++ b/LANCommander.SDK/Helpers/ScriptHelper.cs
@@ -14,15 +14,24 @@ namespace LANCommander.SDK.Helpers
public static readonly ILogger Logger;
public static string SaveTempScript(Script script)
+ {
+ var tempPath = SaveTempScript(script.Contents);
+
+ Logger?.LogTrace("Wrote script {Script} to {Destination}", script.Name, tempPath);
+
+ return tempPath;
+ }
+
+ public static string SaveTempScript(string contents)
{
var tempPath = Path.GetTempFileName();
// PowerShell will only run scripts with the .ps1 file extension
File.Move(tempPath, tempPath + ".ps1");
- Logger?.LogTrace("Writing script {Script} to {Destination}", script.Name, tempPath);
+ tempPath = tempPath + ".ps1";
- File.WriteAllText(tempPath, script.Contents);
+ File.WriteAllText(tempPath, contents);
return tempPath;
}
@@ -37,7 +46,7 @@ namespace LANCommander.SDK.Helpers
if (script.RequiresAdmin)
script.Contents = "# Requires Admin" + "\r\n\r\n" + script.Contents;
- var filename = PowerShellRuntime.GetScriptFilePath(game, type);
+ var filename = GetScriptFilePath(game, type);
if (File.Exists(filename))
File.Delete(filename);
@@ -46,5 +55,24 @@ namespace LANCommander.SDK.Helpers
File.WriteAllText(filename, script.Contents);
}
+
+ public static string GetScriptFilePath(Game game, ScriptType type)
+ {
+ return GetScriptFilePath(game.InstallDirectory, type);
+ }
+
+ public static string GetScriptFilePath(string installDirectory, ScriptType type)
+ {
+ Dictionary filenames = new Dictionary() {
+ { ScriptType.Install, "_install.ps1" },
+ { ScriptType.Uninstall, "_uninstall.ps1" },
+ { ScriptType.NameChange, "_changename.ps1" },
+ { ScriptType.KeyChange, "_changekey.ps1" }
+ };
+
+ var filename = filenames[type];
+
+ return Path.Combine(installDirectory, filename);
+ }
}
}
diff --git a/LANCommander.SDK/LANCommander.SDK.csproj b/LANCommander.SDK/LANCommander.SDK.csproj
index e653767..2e097cf 100644
--- a/LANCommander.SDK/LANCommander.SDK.csproj
+++ b/LANCommander.SDK/LANCommander.SDK.csproj
@@ -6,6 +6,7 @@
+
diff --git a/LANCommander.SDK/PowerShell/PowerShellArgument.cs b/LANCommander.SDK/PowerShell/PowerShellArgument.cs
new file mode 100644
index 0000000..173e861
--- /dev/null
+++ b/LANCommander.SDK/PowerShell/PowerShellArgument.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace LANCommander.SDK.PowerShell
+{
+ public class PowerShellArgument
+ {
+ public string Name { get; set; }
+ public object Value { get; set; }
+ public Type Type { get; set; }
+
+ public PowerShellArgument(string name, object value, Type type)
+ {
+ Name = name;
+ Value = value;
+ Type = type;
+ }
+ }
+}
diff --git a/LANCommander.SDK/PowerShell/PowerShellScript.cs b/LANCommander.SDK/PowerShell/PowerShellScript.cs
new file mode 100644
index 0000000..1d86f51
--- /dev/null
+++ b/LANCommander.SDK/PowerShell/PowerShellScript.cs
@@ -0,0 +1,184 @@
+using LANCommander.SDK.Helpers;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace LANCommander.SDK.PowerShell
+{
+ public class PowerShellScript
+ {
+ private string Contents { get; set; } = "";
+ private string WorkingDirectory { get; set; } = "";
+ private bool AsAdmin { get; set; } = false;
+ private bool ShellExecute { get; set; } = false;
+ private bool IgnoreWow64 { get; set; } = false;
+ private ICollection Variables { get; set; }
+ private Dictionary Arguments { get; set; }
+ private List Modules { get; set; }
+ private Process Process { get; set; }
+
+ public PowerShellScript()
+ {
+ Variables = new List();
+ Arguments = new Dictionary();
+ Modules = new List();
+ Process = new Process();
+
+ Process.StartInfo.FileName = "powershell.exe";
+ Process.StartInfo.RedirectStandardOutput = false;
+
+ AddArgument("ExecutionPolicy", "Unrestricted");
+ AddModule(Path.Combine(Environment.CurrentDirectory, "LANCommander.PowerShell.psd1"));
+ }
+
+ public PowerShellScript UseFile(string path)
+ {
+ Contents = File.ReadAllText(path);
+
+ return this;
+ }
+
+ public PowerShellScript UseInline(string contents)
+ {
+ Contents = contents;
+
+ return this;
+ }
+
+ public PowerShellScript UseWorkingDirectory(string path)
+ {
+ WorkingDirectory = path;
+
+ return this;
+ }
+
+ public PowerShellScript UseShellExecute()
+ {
+ ShellExecute = true;
+
+ return this;
+ }
+
+ public PowerShellScript AddVariable(string name, T value)
+ {
+ Variables.Add(new PowerShellVariable(name, value, typeof(T)));
+
+ return this;
+ }
+
+ public PowerShellScript AddArgument(string name, T value)
+ {
+ Arguments.Add(name, $"\"{value}\"");
+
+ return this;
+ }
+
+ public PowerShellScript AddArgument(string name, int value)
+ {
+ Arguments[name] = value.ToString();
+
+ return this;
+ }
+
+ public PowerShellScript AddArgument(string name, long value)
+ {
+ Arguments[name] = value.ToString();
+
+ return this;
+ }
+
+ public PowerShellScript AddModule(string path)
+ {
+ Modules.Add(path);
+
+ return this;
+ }
+
+ public PowerShellScript RunAsAdmin()
+ {
+ AsAdmin = true;
+
+ Process.StartInfo.Verb = "runas";
+ Process.StartInfo.UseShellExecute = true;
+
+ return this;
+ }
+
+ public PowerShellScript IgnoreWow64Redirection()
+ {
+ IgnoreWow64 = true;
+
+ return this;
+ }
+
+ public int Execute()
+ {
+ var scriptBuilder = new StringBuilder();
+
+ var wow64Value = IntPtr.Zero;
+
+ foreach (var module in Modules)
+ {
+ scriptBuilder.AppendLine($"Import-Module \"{module}\"");
+ }
+
+ foreach (var variable in Variables)
+ {
+ scriptBuilder.AppendLine($"${variable.Name} = ConvertFrom-SerializedBase64 \"{Serialize(variable.Value)}\"");
+ }
+
+ scriptBuilder.AppendLine(Contents);
+
+ var path = ScriptHelper.SaveTempScript(scriptBuilder.ToString());
+
+ AddArgument("File", path);
+
+ if (IgnoreWow64)
+ Wow64DisableWow64FsRedirection(ref wow64Value);
+
+ Process.StartInfo.Arguments = String.Join(" ", Arguments.Select((name, value) =>
+ {
+ return $"-{name} {value}";
+ }));
+
+ if (!String.IsNullOrEmpty(WorkingDirectory))
+ Process.StartInfo.WorkingDirectory = WorkingDirectory;
+
+ if (ShellExecute)
+ Process.StartInfo.UseShellExecute = true;
+
+ if (AsAdmin)
+ {
+ Process.StartInfo.Verb = "runas";
+ Process.StartInfo.UseShellExecute = true;
+ }
+
+ Process.Start();
+ Process.WaitForExit();
+
+ if (IgnoreWow64)
+ Wow64RevertWow64FsRedirection(ref wow64Value);
+
+ if (File.Exists(path))
+ File.Delete(path);
+
+ return Process.ExitCode;
+ }
+
+ public static string Serialize(T input)
+ {
+ // Use the PowerShell serializer to generate XML for our input. Then convert to base64 so we can put it on one line.
+ return Convert.ToBase64String(Encoding.UTF8.GetBytes(System.Management.Automation.PSSerializer.Serialize(input)));
+ }
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ static extern bool Wow64DisableWow64FsRedirection(ref IntPtr ptr);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ static extern bool Wow64RevertWow64FsRedirection(ref IntPtr ptr);
+ }
+}
diff --git a/LANCommander.SDK/PowerShell/PowerShellVariable.cs b/LANCommander.SDK/PowerShell/PowerShellVariable.cs
new file mode 100644
index 0000000..a7f78b8
--- /dev/null
+++ b/LANCommander.SDK/PowerShell/PowerShellVariable.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace LANCommander.SDK.PowerShell
+{
+ public class PowerShellVariable
+ {
+ public string Name { get; set; }
+ public object Value { get; set; }
+ public Type Type { get; set; }
+
+ public PowerShellVariable(string name, object value, Type type)
+ {
+ Name = name;
+ Value = value;
+ Type = type;
+ }
+ }
+}
diff --git a/LANCommander.SDK/PowerShellRuntime.cs b/LANCommander.SDK/PowerShellRuntime.cs
deleted file mode 100644
index 18d8f8c..0000000
--- a/LANCommander.SDK/PowerShellRuntime.cs
+++ /dev/null
@@ -1,173 +0,0 @@
-using LANCommander.SDK.Enums;
-using LANCommander.SDK.Models;
-using Microsoft.Extensions.Logging;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace LANCommander.SDK
-{
- public static class PowerShellRuntime
- {
- public static readonly ILogger Logger;
-
- [DllImport("kernel32.dll", SetLastError = true)]
- static extern bool Wow64DisableWow64FsRedirection(ref IntPtr ptr);
-
- [DllImport("kernel32.dll", SetLastError = true)]
- static extern bool Wow64RevertWow64FsRedirection(ref IntPtr ptr);
-
- public static void RunCommand(string command, bool asAdmin = false)
- {
- Logger?.LogTrace($"Executing command `{command}` | Admin: {asAdmin}");
-
- var tempScript = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + ".ps1");
-
- Logger?.LogTrace($"Creating temp script at path {tempScript}");
-
- File.WriteAllText(tempScript, command);
-
- RunScript(tempScript, asAdmin);
-
- File.Delete(tempScript);
- }
-
- public static int RunScript(string path, bool asAdmin = false, string arguments = null, string workingDirectory = null)
- {
- Logger?.LogTrace($"Executing script at path {path} | Admin: {asAdmin} | Arguments: {arguments}");
-
- var wow64Value = IntPtr.Zero;
-
- // Disable Wow64 redirection so we can hit areas of the registry absolutely
- Wow64DisableWow64FsRedirection(ref wow64Value);
-
- var process = new Process();
-
- process.StartInfo.FileName = "powershell.exe";
- process.StartInfo.Arguments = $@"-ExecutionPolicy Unrestricted -File ""{path}""";
- process.StartInfo.UseShellExecute = false;
- process.StartInfo.RedirectStandardOutput = false;
-
- if (arguments != null)
- process.StartInfo.Arguments += " " + arguments;
-
- if (workingDirectory != null)
- process.StartInfo.WorkingDirectory = workingDirectory;
-
- if (asAdmin)
- {
- process.StartInfo.Verb = "runas";
- process.StartInfo.UseShellExecute = true;
- }
-
- process.Start();
- process.WaitForExit();
-
- Wow64RevertWow64FsRedirection(ref wow64Value);
-
- return process.ExitCode;
- }
-
- public static void RunScript(Game game, ScriptType type, string arguments = null)
- {
- RunScript(game.InstallDirectory, type, arguments);
- }
-
- public static void RunScript(string installDirectory, ScriptType type, string arguments = null)
- {
- var path = GetScriptFilePath(installDirectory, type);
-
- if (File.Exists(path))
- {
- var contents = File.ReadAllText(path);
-
- if (contents.StartsWith("# Requires Admin"))
- RunScript(path, true, arguments);
- else
- RunScript(path, false, arguments);
- }
- }
-
- public static void RunScriptsAsAdmin(IEnumerable paths, string arguments = null)
- {
- // Concatenate scripts
- var sb = new StringBuilder();
-
- Logger?.LogTrace("Concatenating scripts...");
-
- foreach (var path in paths)
- {
- var contents = File.ReadAllText(path);
-
- sb.AppendLine(contents);
-
- Logger?.LogTrace($"Added {path}!");
- }
-
- Logger?.LogTrace("Done concatenating!");
-
- if (sb.Length > 0)
- {
- var scriptPath = Path.GetTempFileName();
-
- Logger?.LogTrace($"Creating temp script at path {scriptPath}");
-
- File.WriteAllText(scriptPath, sb.ToString());
-
- RunScript(scriptPath, true, arguments);
- }
- }
-
- public static void RunScripts(IEnumerable installDirectories, ScriptType type, string arguments = null)
- {
- List scripts = new List();
- List adminScripts = new List();
-
- foreach (var installDirectory in installDirectories)
- {
- var path = GetScriptFilePath(installDirectory, type);
-
- if (!File.Exists(path))
- continue;
-
- var contents = File.ReadAllText(path);
-
- if (contents.StartsWith("# Requires Admin"))
- adminScripts.Add(path);
- else
- scripts.Add(path);
- }
-
- RunScriptsAsAdmin(adminScripts, arguments);
-
- foreach (var script in scripts)
- {
- RunScript(script, false, arguments);
- }
- }
-
- public static string GetScriptFilePath(Game game, ScriptType type)
- {
- return GetScriptFilePath(game.InstallDirectory, type);
- }
-
- public static string GetScriptFilePath(string installDirectory, ScriptType type)
- {
- Dictionary filenames = new Dictionary() {
- { ScriptType.Install, "_install.ps1" },
- { ScriptType.Uninstall, "_uninstall.ps1" },
- { ScriptType.NameChange, "_changename.ps1" },
- { ScriptType.KeyChange, "_changekey.ps1" }
- };
-
- var filename = filenames[type];
-
- return Path.Combine(installDirectory, filename);
- }
- }
-}
diff --git a/LANCommander.SDK/RedistributableManager.cs b/LANCommander.SDK/RedistributableManager.cs
index fcc87d8..699b5af 100644
--- a/LANCommander.SDK/RedistributableManager.cs
+++ b/LANCommander.SDK/RedistributableManager.cs
@@ -2,6 +2,7 @@
using LANCommander.SDK.Extensions;
using LANCommander.SDK.Helpers;
using LANCommander.SDK.Models;
+using LANCommander.SDK.PowerShell;
using Microsoft.Extensions.Logging;
using SharpCompress.Common;
using SharpCompress.Readers;
@@ -57,7 +58,7 @@ namespace LANCommander.SDK
var detectionScript = redistributable.Scripts.FirstOrDefault(s => s.Type == ScriptType.DetectInstall);
detectionScriptTempFile = ScriptHelper.SaveTempScript(detectionScript);
- var detectionResult = PowerShellRuntime.RunScript(detectionScriptTempFile, detectionScript.RequiresAdmin);
+ var detectionResult = RunScript(detectionScriptTempFile, redistributable);
// Redistributable is not installed
if (detectionResult == 0)
@@ -70,12 +71,12 @@ namespace LANCommander.SDK
{
extractTempPath = extractionResult.Directory;
- PowerShellRuntime.RunScript(installScriptTempFile, installScript.RequiresAdmin, null, extractTempPath);
+ RunScript(installScriptTempFile, redistributable, installScript.RequiresAdmin, extractTempPath);
}
}
else
{
- PowerShellRuntime.RunScript(installScriptTempFile, installScript.RequiresAdmin, null, extractTempPath);
+ RunScript(installScriptTempFile, redistributable, installScript.RequiresAdmin, extractTempPath);
}
}
}
@@ -164,5 +165,20 @@ namespace LANCommander.SDK
return extractionResult;
}
+
+ private int RunScript(string path, Redistributable redistributable, bool requiresAdmin = false, string workingDirectory = "")
+ {
+ var script = new PowerShellScript();
+
+ script.AddVariable("Redistributable", redistributable);
+
+ script.UseWorkingDirectory(workingDirectory);
+ script.UseFile(path);
+
+ if (requiresAdmin)
+ script.RunAsAdmin();
+
+ return script.Execute();
+ }
}
}
diff --git a/LANCommander.sln b/LANCommander.sln
index 529f8cc..f77170c 100644
--- a/LANCommander.sln
+++ b/LANCommander.sln
@@ -11,6 +11,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LANCommander.SDK", "LANComm
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LANCommander.PCGamingWiki", "LANCommander.PCGamingWiki\LANCommander.PCGamingWiki.csproj", "{2436B817-4475-4E70-9BB2-E1E7866DB79F}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LANCommander.PowerShell", "LANCommander.PowerShell\LANCommander.PowerShell.csproj", "{807943BF-0C7D-4ED3-8393-CFEE64E3138C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LANCommander.PowerShell.Tests", "LANCommander.PowerShell.Tests\LANCommander.PowerShell.Tests.csproj", "{D7069A13-F0AA-4CBF-9013-4276F130A6DD}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -33,6 +37,14 @@ Global
{2436B817-4475-4E70-9BB2-E1E7866DB79F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2436B817-4475-4E70-9BB2-E1E7866DB79F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2436B817-4475-4E70-9BB2-E1E7866DB79F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {807943BF-0C7D-4ED3-8393-CFEE64E3138C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {807943BF-0C7D-4ED3-8393-CFEE64E3138C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {807943BF-0C7D-4ED3-8393-CFEE64E3138C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {807943BF-0C7D-4ED3-8393-CFEE64E3138C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D7069A13-F0AA-4CBF-9013-4276F130A6DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D7069A13-F0AA-4CBF-9013-4276F130A6DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D7069A13-F0AA-4CBF-9013-4276F130A6DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D7069A13-F0AA-4CBF-9013-4276F130A6DD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/LANCommander/Migrations/20231117064657_DeleteDeprecatedSnippets.Designer.cs b/LANCommander/Migrations/20231117064657_DeleteDeprecatedSnippets.Designer.cs
new file mode 100644
index 0000000..f143973
--- /dev/null
+++ b/LANCommander/Migrations/20231117064657_DeleteDeprecatedSnippets.Designer.cs
@@ -0,0 +1,1691 @@
+//
+using System;
+using LANCommander.Data;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace LANCommander.Migrations
+{
+ [DbContext(typeof(DatabaseContext))]
+ [Migration("20231117064657_DeleteDeprecatedSnippets")]
+ partial class DeleteDeprecatedSnippets
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "7.0.13")
+ .HasAnnotation("Proxies:ChangeTracking", false)
+ .HasAnnotation("Proxies:CheckEquality", false)
+ .HasAnnotation("Proxies:LazyLoading", true);
+
+ modelBuilder.Entity("CategoryGame", b =>
+ {
+ b.Property("CategoriesId")
+ .HasColumnType("TEXT");
+
+ b.Property("GamesId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("CategoriesId", "GamesId");
+
+ b.HasIndex("GamesId");
+
+ b.ToTable("CategoryGame");
+ });
+
+ modelBuilder.Entity("GameDeveloper", b =>
+ {
+ b.Property("DeveloperId")
+ .HasColumnType("TEXT");
+
+ b.Property("GameId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("DeveloperId", "GameId");
+
+ b.HasIndex("GameId");
+
+ b.ToTable("GameDeveloper");
+ });
+
+ modelBuilder.Entity("GameGenre", b =>
+ {
+ b.Property("GamesId")
+ .HasColumnType("TEXT");
+
+ b.Property("GenresId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("GamesId", "GenresId");
+
+ b.HasIndex("GenresId");
+
+ b.ToTable("GameGenre");
+ });
+
+ modelBuilder.Entity("GamePublisher", b =>
+ {
+ b.Property("GameId")
+ .HasColumnType("TEXT");
+
+ b.Property("PublisherId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("GameId", "PublisherId");
+
+ b.HasIndex("PublisherId");
+
+ b.ToTable("GamePublisher");
+ });
+
+ modelBuilder.Entity("GameRedistributable", b =>
+ {
+ b.Property("GameId")
+ .HasColumnType("TEXT");
+
+ b.Property("RedistributableId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("GameId", "RedistributableId");
+
+ b.HasIndex("RedistributableId");
+
+ b.ToTable("GameRedistributable");
+ });
+
+ modelBuilder.Entity("GameTag", b =>
+ {
+ b.Property("GamesId")
+ .HasColumnType("TEXT");
+
+ b.Property("TagsId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("GamesId", "TagsId");
+
+ b.HasIndex("TagsId");
+
+ b.ToTable("GameTag");
+ });
+
+ modelBuilder.Entity("LANCommander.Data.Models.Action", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("Arguments")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("GameId")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Path")
+ .HasColumnType("TEXT");
+
+ b.Property("PrimaryAction")
+ .HasColumnType("INTEGER");
+
+ b.Property("SortOrder")
+ .HasColumnType("INTEGER");
+
+ b.Property("UpdatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("WorkingDirectory")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedById");
+
+ b.HasIndex("GameId");
+
+ b.HasIndex("UpdatedById");
+
+ b.ToTable("Actions");
+ });
+
+ modelBuilder.Entity("LANCommander.Data.Models.Archive", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("Changelog")
+ .HasColumnType("TEXT");
+
+ b.Property("CompressedSize")
+ .HasColumnType("INTEGER");
+
+ b.Property("CreatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("GameId")
+ .HasColumnType("TEXT");
+
+ b.Property("LastVersionId")
+ .HasColumnType("TEXT");
+
+ b.Property("ObjectKey")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("RedistributableId")
+ .HasColumnType("TEXT");
+
+ b.Property("UncompressedSize")
+ .HasColumnType("INTEGER");
+
+ b.Property("UpdatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("Version")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedById");
+
+ b.HasIndex("GameId");
+
+ b.HasIndex("LastVersionId");
+
+ b.HasIndex("RedistributableId");
+
+ b.HasIndex("UpdatedById");
+
+ b.ToTable("Archive");
+ });
+
+ modelBuilder.Entity("LANCommander.Data.Models.Category", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("ParentId")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedOn")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedById");
+
+ b.HasIndex("ParentId");
+
+ b.HasIndex("UpdatedById");
+
+ b.ToTable("Categories");
+ });
+
+ modelBuilder.Entity("LANCommander.Data.Models.Company", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedOn")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedById");
+
+ b.HasIndex("UpdatedById");
+
+ b.ToTable("Companies");
+ });
+
+ modelBuilder.Entity("LANCommander.Data.Models.Game", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("Description")
+ .HasColumnType("TEXT");
+
+ b.Property("DirectoryName")
+ .HasColumnType("TEXT");
+
+ b.Property("IGDBId")
+ .HasColumnType("INTEGER");
+
+ b.Property("Notes")
+ .HasColumnType("TEXT");
+
+ b.Property("ReleasedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("Singleplayer")
+ .HasColumnType("INTEGER");
+
+ b.Property("SortTitle")
+ .HasColumnType("TEXT");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("ValidKeyRegex")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedById");
+
+ b.HasIndex("UpdatedById");
+
+ b.ToTable("Games");
+ });
+
+ modelBuilder.Entity("LANCommander.Data.Models.GameSave", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("GameId")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("UserId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedById");
+
+ b.HasIndex("GameId");
+
+ b.HasIndex("UpdatedById");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("GameSaves");
+ });
+
+ modelBuilder.Entity("LANCommander.Data.Models.Genre", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedOn")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedById");
+
+ b.HasIndex("UpdatedById");
+
+ b.ToTable("Genres");
+ });
+
+ modelBuilder.Entity("LANCommander.Data.Models.Key", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("AllocationMethod")
+ .HasColumnType("INTEGER");
+
+ b.Property("ClaimedByComputerName")
+ .HasMaxLength(255)
+ .HasColumnType("TEXT");
+
+ b.Property("ClaimedByIpv4Address")
+ .HasMaxLength(15)
+ .HasColumnType("TEXT");
+
+ b.Property("ClaimedByMacAddress")
+ .HasMaxLength(17)
+ .HasColumnType("TEXT");
+
+ b.Property("ClaimedByUserId")
+ .HasColumnType("TEXT");
+
+ b.Property("ClaimedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("GameId")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("Value")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ClaimedByUserId");
+
+ b.HasIndex("CreatedById");
+
+ b.HasIndex("GameId");
+
+ b.HasIndex("UpdatedById");
+
+ b.ToTable("Keys");
+ });
+
+ modelBuilder.Entity("LANCommander.Data.Models.Media", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("FileId")
+ .HasColumnType("TEXT");
+
+ b.Property("GameId")
+ .HasColumnType("TEXT");
+
+ b.Property("MimeType")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("TEXT");
+
+ b.Property("SourceUrl")
+ .IsRequired()
+ .HasMaxLength(2048)
+ .HasColumnType("TEXT");
+
+ b.Property("Type")
+ .HasColumnType("INTEGER");
+
+ b.Property("UpdatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedOn")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedById");
+
+ b.HasIndex("GameId");
+
+ b.HasIndex("UpdatedById");
+
+ b.ToTable("Media");
+ });
+
+ modelBuilder.Entity("LANCommander.Data.Models.MultiplayerMode", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("Description")
+ .HasColumnType("TEXT");
+
+ b.Property("GameId")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("MaxPlayers")
+ .HasColumnType("INTEGER");
+
+ b.Property("MinPlayers")
+ .HasColumnType("INTEGER");
+
+ b.Property("NetworkProtocol")
+ .HasColumnType("INTEGER");
+
+ b.Property("Spectators")
+ .HasColumnType("INTEGER");
+
+ b.Property("Type")
+ .HasColumnType("INTEGER");
+
+ b.Property("UpdatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedOn")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedById");
+
+ b.HasIndex("GameId");
+
+ b.HasIndex("UpdatedById");
+
+ b.ToTable("MultiplayerModes");
+ });
+
+ modelBuilder.Entity("LANCommander.Data.Models.Redistributable", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("Description")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Notes")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedOn")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedById");
+
+ b.HasIndex("UpdatedById");
+
+ b.ToTable("Redistributables");
+ });
+
+ modelBuilder.Entity("LANCommander.Data.Models.Role", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("NormalizedName")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NormalizedName")
+ .IsUnique()
+ .HasDatabaseName("RoleNameIndex");
+
+ b.ToTable("AspNetRoles", (string)null);
+ });
+
+ modelBuilder.Entity("LANCommander.Data.Models.SavePath", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("GameId")
+ .HasColumnType("TEXT");
+
+ b.Property("Path")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Type")
+ .HasColumnType("INTEGER");
+
+ b.Property("UpdatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedOn")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedById");
+
+ b.HasIndex("GameId");
+
+ b.HasIndex("UpdatedById");
+
+ b.ToTable("SavePaths");
+ });
+
+ modelBuilder.Entity("LANCommander.Data.Models.Script", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("Contents")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("Description")
+ .HasColumnType("TEXT");
+
+ b.Property("GameId")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("RedistributableId")
+ .HasColumnType("TEXT");
+
+ b.Property("RequiresAdmin")
+ .HasColumnType("INTEGER");
+
+ b.Property("Type")
+ .HasColumnType("INTEGER");
+
+ b.Property("UpdatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedOn")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedById");
+
+ b.HasIndex("GameId");
+
+ b.HasIndex("RedistributableId");
+
+ b.HasIndex("UpdatedById");
+
+ b.ToTable("Scripts");
+ });
+
+ modelBuilder.Entity("LANCommander.Data.Models.Server", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("Arguments")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Autostart")
+ .HasColumnType("INTEGER");
+
+ b.Property("AutostartDelay")
+ .HasColumnType("INTEGER");
+
+ b.Property("CreatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("GameId")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("OnStartScriptPath")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("OnStopScriptPath")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Path")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("UseShellExecute")
+ .HasColumnType("INTEGER");
+
+ b.Property("WorkingDirectory")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedById");
+
+ b.HasIndex("GameId");
+
+ b.HasIndex("UpdatedById");
+
+ b.ToTable("Servers");
+ });
+
+ modelBuilder.Entity("LANCommander.Data.Models.ServerConsole", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("Host")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Password")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Path")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Port")
+ .HasColumnType("INTEGER");
+
+ b.Property("ServerId")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("ServerId1")
+ .HasColumnType("TEXT");
+
+ b.Property("Type")
+ .HasColumnType("INTEGER");
+
+ b.Property("UpdatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedOn")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedById");
+
+ b.HasIndex("ServerId");
+
+ b.HasIndex("ServerId1");
+
+ b.HasIndex("UpdatedById");
+
+ b.ToTable("ServerConsoles");
+ });
+
+ modelBuilder.Entity("LANCommander.Data.Models.ServerHttpPath", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("LocalPath")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Path")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("ServerId")
+ .HasColumnType("TEXT");
+
+ b.Property("ServerId1")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedOn")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedById");
+
+ b.HasIndex("ServerId");
+
+ b.HasIndex("ServerId1");
+
+ b.HasIndex("UpdatedById");
+
+ b.ToTable("ServerHttpPath");
+ });
+
+ modelBuilder.Entity("LANCommander.Data.Models.Tag", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedById")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedOn")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedById");
+
+ b.HasIndex("UpdatedById");
+
+ b.ToTable("Tags");
+ });
+
+ modelBuilder.Entity("LANCommander.Data.Models.User", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("AccessFailedCount")
+ .HasColumnType("INTEGER");
+
+ b.Property("Alias")
+ .HasColumnType("TEXT");
+
+ b.Property("Approved")
+ .HasColumnType("INTEGER");
+
+ b.Property("ApprovedOn")
+ .HasColumnType("TEXT");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("TEXT");
+
+ b.Property("Email")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("EmailConfirmed")
+ .HasColumnType("INTEGER");
+
+ b.Property("LockoutEnabled")
+ .HasColumnType("INTEGER");
+
+ b.Property("LockoutEnd")
+ .HasColumnType("TEXT");
+
+ b.Property("NormalizedEmail")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("NormalizedUserName")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("PasswordHash")
+ .HasColumnType("TEXT");
+
+ b.Property("PhoneNumber")
+ .HasColumnType("TEXT");
+
+ b.Property("PhoneNumberConfirmed")
+ .HasColumnType("INTEGER");
+
+ b.Property("RefreshToken")
+ .HasColumnType("TEXT");
+
+ b.Property("RefreshTokenExpiration")
+ .HasColumnType("TEXT");
+
+ b.Property("SecurityStamp")
+ .HasColumnType("TEXT");
+
+ b.Property("TwoFactorEnabled")
+ .HasColumnType("INTEGER");
+
+ b.Property("UserName")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NormalizedEmail")
+ .HasDatabaseName("EmailIndex");
+
+ b.HasIndex("NormalizedUserName")
+ .IsUnique()
+ .HasDatabaseName("UserNameIndex");
+
+ b.ToTable("AspNetUsers", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("ClaimType")
+ .HasColumnType("TEXT");
+
+ b.Property("ClaimValue")
+ .HasColumnType("TEXT");
+
+ b.Property("RoleId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetRoleClaims", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("ClaimType")
+ .HasColumnType("TEXT");
+
+ b.Property("ClaimValue")
+ .HasColumnType("TEXT");
+
+ b.Property("UserId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AspNetUserClaims", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b =>
+ {
+ b.Property("LoginProvider")
+ .HasMaxLength(128)
+ .HasColumnType("TEXT");
+
+ b.Property("ProviderKey")
+ .HasMaxLength(128)
+ .HasColumnType("TEXT");
+
+ b.Property("ProviderDisplayName")
+ .HasColumnType("TEXT");
+
+ b.Property("UserId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("LoginProvider", "ProviderKey");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AspNetUserLogins", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("TEXT");
+
+ b.Property("RoleId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("UserId", "RoleId");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetUserRoles", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("TEXT");
+
+ b.Property("LoginProvider")
+ .HasMaxLength(128)
+ .HasColumnType("TEXT");
+
+ b.Property