diff --git a/LANCommander.Playnite.Extension/LANCommander.PlaynitePlugin.csproj b/LANCommander.Playnite.Extension/LANCommander.PlaynitePlugin.csproj
index bbd6a13..e8bed07 100644
--- a/LANCommander.Playnite.Extension/LANCommander.PlaynitePlugin.csproj
+++ b/LANCommander.Playnite.Extension/LANCommander.PlaynitePlugin.csproj
@@ -42,6 +42,9 @@
+
+ ..\packages\SharpCompress.0.34.1\lib\net462\SharpCompress.dll
+
..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll
@@ -85,10 +88,13 @@
+
+ ..\packages\ZstdSharp.Port.0.7.2\lib\net461\ZstdSharp.dll
+
-
+
diff --git a/LANCommander.Playnite.Extension/LANCommanderLibraryPlugin.cs b/LANCommander.Playnite.Extension/LANCommanderLibraryPlugin.cs
index fee02ea..a7da90f 100644
--- a/LANCommander.Playnite.Extension/LANCommanderLibraryPlugin.cs
+++ b/LANCommander.Playnite.Extension/LANCommanderLibraryPlugin.cs
@@ -1,5 +1,4 @@
using LANCommander.PlaynitePlugin.Extensions;
-using LANCommander.PlaynitePlugin.Services;
using Playnite.SDK;
using Playnite.SDK.Events;
using Playnite.SDK.Models;
@@ -22,7 +21,7 @@ namespace LANCommander.PlaynitePlugin
public static readonly ILogger Logger = LogManager.GetLogger();
internal LANCommanderSettingsViewModel Settings { get; set; }
internal LANCommander.SDK.Client LANCommanderClient { get; set; }
- internal GameSaveService GameSaveService { get; set; }
+ internal LANCommanderSaveController SaveController { get; set; }
public override Guid Id { get; } = Guid.Parse("48e1bac7-e0a0-45d7-ba83-36f5e9e959fc");
public override string Name => "LANCommander";
@@ -333,12 +332,12 @@ namespace LANCommander.PlaynitePlugin
public override void OnGameStarting(OnGameStartingEventArgs args)
{
- GameSaveService.DownloadSave(args.Game);
+ SaveController.Download(args.Game);
}
public override void OnGameStopped(OnGameStoppedEventArgs args)
{
- GameSaveService.UploadSave(args.Game);
+ SaveController.Upload(args.Game);
}
public override IEnumerable GetTopPanelItems()
diff --git a/LANCommander.Playnite.Extension/SaveController.cs b/LANCommander.Playnite.Extension/SaveController.cs
new file mode 100644
index 0000000..557ee2e
--- /dev/null
+++ b/LANCommander.Playnite.Extension/SaveController.cs
@@ -0,0 +1,61 @@
+using LANCommander.SDK;
+using Playnite.SDK;
+using Playnite.SDK.Models;
+using Playnite.SDK.Plugins;
+
+namespace LANCommander.PlaynitePlugin
+{
+ public class LANCommanderSaveController : ControllerBase
+ {
+ private static readonly ILogger Logger;
+
+ private LANCommanderLibraryPlugin Plugin;
+
+ public LANCommanderSaveController(LANCommanderLibraryPlugin plugin, Game game) : base(game)
+ {
+ Name = "Download save using LANCommander";
+ Plugin = plugin;
+ }
+
+ public void Download(Game game)
+ {
+ if (game != null)
+ {
+ Plugin.PlayniteApi.Dialogs.ActivateGlobalProgress(progress =>
+ {
+ progress.ProgressMaxValue = 100;
+ progress.CurrentProgressValue = 0;
+
+ var saveManager = new GameSaveManager(Plugin.LANCommanderClient);
+
+ saveManager.OnDownloadProgress += (downloadProgress) =>
+ {
+ progress.CurrentProgressValue = downloadProgress.ProgressPercentage;
+ };
+
+ saveManager.OnDownloadComplete += (downloadComplete) =>
+ {
+ progress.CurrentProgressValue = 100;
+ };
+
+ saveManager.Download(game.InstallDirectory);
+
+ // Lock the thread until the download is done
+ while (progress.CurrentProgressValue != 100) { }
+ },
+ new GlobalProgressOptions("Downloading latest save...")
+ {
+ IsIndeterminate = false,
+ Cancelable = false
+ });
+ }
+ }
+
+ public void Upload(Game game)
+ {
+ var saveManager = new GameSaveManager(Plugin.LANCommanderClient);
+
+ saveManager.Upload(game.InstallDirectory);
+ }
+ }
+}
diff --git a/LANCommander.Playnite.Extension/Views/Authentication.xaml.cs b/LANCommander.Playnite.Extension/Views/Authentication.xaml.cs
index 6b3db4f..0e6869a 100644
--- a/LANCommander.Playnite.Extension/Views/Authentication.xaml.cs
+++ b/LANCommander.Playnite.Extension/Views/Authentication.xaml.cs
@@ -100,24 +100,16 @@ namespace LANCommander.PlaynitePlugin.Views
LoginButton.Content = "Logging in...";
}));
- if (Plugin.LANCommander == null || Plugin.LANCommander.Client == null)
- Plugin.LANCommander = new LANCommanderClient(Context.ServerAddress);
- else
- Plugin.LANCommander.Client.BaseUrl = new Uri(Context.ServerAddress);
+ if (Plugin.LANCommanderClient == null)
+ Plugin.LANCommanderClient = new LANCommander.SDK.Client(Context.ServerAddress);
- var response = await Plugin.LANCommander.AuthenticateAsync(Context.UserName, Context.Password);
+ var response = await Plugin.LANCommanderClient.AuthenticateAsync(Context.UserName, Context.Password);
Plugin.Settings.ServerAddress = Context.ServerAddress;
Plugin.Settings.AccessToken = response.AccessToken;
Plugin.Settings.RefreshToken = response.RefreshToken;
- Plugin.LANCommander.Token = new AuthToken()
- {
- AccessToken = response.AccessToken,
- RefreshToken = response.RefreshToken,
- };
-
- var profile = Plugin.LANCommander.GetProfile();
+ var profile = Plugin.LANCommanderClient.GetProfile();
Plugin.Settings.PlayerName = String.IsNullOrWhiteSpace(profile.Alias) ? profile.UserName : profile.Alias;
@@ -148,24 +140,16 @@ namespace LANCommander.PlaynitePlugin.Views
RegisterButton.IsEnabled = false;
RegisterButton.Content = "Working...";
- if (Plugin.LANCommander == null || Plugin.LANCommander.Client == null)
- Plugin.LANCommander = new LANCommanderClient(Context.ServerAddress);
- else
- Plugin.LANCommander.Client.BaseUrl = new Uri(Context.ServerAddress);
+ if (Plugin.LANCommanderClient == null)
+ Plugin.LANCommanderClient = new LANCommander.SDK.Client(Context.ServerAddress);
- var response = await Plugin.LANCommander.RegisterAsync(Context.UserName, Context.Password);
+ var response = await Plugin.LANCommanderClient.RegisterAsync(Context.UserName, Context.Password);
Plugin.Settings.ServerAddress = Context.ServerAddress;
Plugin.Settings.AccessToken = response.AccessToken;
Plugin.Settings.RefreshToken = response.RefreshToken;
Plugin.Settings.PlayerName = Context.UserName;
- Plugin.LANCommander.Token = new AuthToken()
- {
- AccessToken = response.AccessToken,
- RefreshToken = response.RefreshToken,
- };
-
Context.Password = String.Empty;
Plugin.SavePluginSettings(Plugin.Settings);
diff --git a/LANCommander.Playnite.Extension/Views/LANCommanderSettingsView.xaml.cs b/LANCommander.Playnite.Extension/Views/LANCommanderSettingsView.xaml.cs
index 2388840..44526a0 100644
--- a/LANCommander.Playnite.Extension/Views/LANCommanderSettingsView.xaml.cs
+++ b/LANCommander.Playnite.Extension/Views/LANCommanderSettingsView.xaml.cs
@@ -47,7 +47,7 @@ namespace LANCommander.PlaynitePlugin
RefreshToken = Settings.RefreshToken,
};
- var task = Task.Run(() => Plugin.LANCommander.ValidateToken(token))
+ var task = Task.Run(() => Plugin.LANCommanderClient.ValidateToken(token))
.ContinueWith(antecedent =>
{
try
@@ -90,7 +90,7 @@ namespace LANCommander.PlaynitePlugin
{
Plugin.Settings.AccessToken = String.Empty;
Plugin.Settings.RefreshToken = String.Empty;
- Plugin.LANCommander.Token = null;
+ Plugin.LANCommanderClient.UseToken(null);
Plugin.SavePluginSettings(Plugin.Settings);
diff --git a/LANCommander.SDK/Client.cs b/LANCommander.SDK/Client.cs
index 958e8ca..805b0b4 100644
--- a/LANCommander.SDK/Client.cs
+++ b/LANCommander.SDK/Client.cs
@@ -107,7 +107,7 @@ namespace LANCommander.SDK
}
}
- public async Task RegisterAsync(string username, string password)
+ public async Task RegisterAsync(string username, string password)
{
var response = await ApiClient.ExecuteAsync(new RestRequest("/api/auth/register", Method.POST).AddJsonBody(new AuthRequest()
{
@@ -118,7 +118,14 @@ namespace LANCommander.SDK
switch (response.StatusCode)
{
case HttpStatusCode.OK:
- return response.Data;
+ Token = new AuthToken
+ {
+ AccessToken = response.Data.AccessToken,
+ RefreshToken = response.Data.RefreshToken,
+ Expiration = response.Data.Expiration
+ };
+
+ return Token;
case HttpStatusCode.BadRequest:
case HttpStatusCode.Forbidden:
@@ -137,7 +144,7 @@ namespace LANCommander.SDK
return response.StatusCode == HttpStatusCode.OK;
}
- public AuthResponse RefreshToken(AuthToken token)
+ public AuthToken RefreshToken(AuthToken token)
{
Logger.LogTrace("Refreshing token...");
@@ -149,7 +156,14 @@ namespace LANCommander.SDK
if (response.StatusCode != HttpStatusCode.OK)
throw new WebException(response.ErrorMessage);
- return response.Data;
+ Token = new AuthToken
+ {
+ AccessToken = response.Data.AccessToken,
+ RefreshToken = response.Data.RefreshToken,
+ Expiration = response.Data.Expiration
+ };
+
+ return Token;
}
public bool ValidateToken()
diff --git a/LANCommander.Playnite.Extension/Services/GameSaveService.cs b/LANCommander.SDK/GameSaveManager.cs
similarity index 52%
rename from LANCommander.Playnite.Extension/Services/GameSaveService.cs
rename to LANCommander.SDK/GameSaveManager.cs
index 4513443..a7ffc1b 100644
--- a/LANCommander.Playnite.Extension/Services/GameSaveService.cs
+++ b/LANCommander.SDK/GameSaveManager.cs
@@ -1,64 +1,55 @@
using LANCommander.SDK;
-using Playnite.SDK;
-using Playnite.SDK.Models;
+using LANCommander.SDK.Helpers;
+using LANCommander.SDK.Models;
using SharpCompress.Archives;
using SharpCompress.Archives.Zip;
using SharpCompress.Common;
using SharpCompress.Readers;
using System;
using System.Collections.Generic;
+using System.ComponentModel;
using System.IO;
using System.Linq;
+using System.Net;
using System.Text;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
-namespace LANCommander.PlaynitePlugin.Services
+namespace LANCommander.SDK
{
- internal class GameSaveService
+ public class GameSaveManager
{
- private readonly LANCommander.SDK.Client LANCommander;
- private readonly IPlayniteAPI PlayniteApi;
+ private readonly Client Client;
- internal GameSaveService(LANCommander.SDK.Client lanCommander, IPlayniteAPI playniteApi)
+ public delegate void OnDownloadProgressHandler(DownloadProgressChangedEventArgs e);
+ public event OnDownloadProgressHandler OnDownloadProgress;
+
+ public delegate void OnDownloadCompleteHandler(AsyncCompletedEventArgs e);
+ public event OnDownloadCompleteHandler OnDownloadComplete;
+
+ public GameSaveManager(Client client)
{
- LANCommander = lanCommander;
- PlayniteApi = playniteApi;
+ Client = client;
}
- internal void DownloadSave(Game game)
+ public void Download(string installDirectory)
{
+ var manifest = ManifestHelper.Read(installDirectory);
+
string tempFile = String.Empty;
- if (game != null)
+ if (manifest != null)
{
- PlayniteApi.Dialogs.ActivateGlobalProgress(progress =>
+ var destination = Client.DownloadLatestSave(manifest.Id, (changed) =>
{
- progress.ProgressMaxValue = 100;
- progress.CurrentProgressValue = 0;
-
- var destination = LANCommander.DownloadLatestSave(Guid.Parse(game.GameId), (changed) =>
- {
- progress.CurrentProgressValue = changed.ProgressPercentage;
- }, (complete) =>
- {
- progress.CurrentProgressValue = 100;
- });
-
- // Lock the thread until download is done
- while (progress.CurrentProgressValue != 100)
- {
-
- }
-
- tempFile = destination;
- },
- new GlobalProgressOptions("Downloading latest save...")
+ OnDownloadProgress?.Invoke(changed);
+ }, (complete) =>
{
- IsIndeterminate = false,
- Cancelable = false
+ OnDownloadComplete?.Invoke(complete);
});
+ tempFile = destination;
+
// Go into the archive and extract the files to the correct locations
try
{
@@ -72,10 +63,6 @@ namespace LANCommander.PlaynitePlugin.Services
.WithNamingConvention(new PascalCaseNamingConvention())
.Build();
- var manifestContents = File.ReadAllText(Path.Combine(tempLocation, "_manifest.yml"));
-
- var manifest = deserializer.Deserialize(manifestContents);
-
#region Move files
foreach (var savePath in manifest.SavePaths.Where(sp => sp.Type == "File"))
{
@@ -84,7 +71,7 @@ namespace LANCommander.PlaynitePlugin.Services
var tempSavePathFile = Path.Combine(tempSavePath, savePath.Path.Replace('/', '\\').Replace("{InstallDir}\\", ""));
- var destination = Environment.ExpandEnvironmentVariables(savePath.Path.Replace('/', '\\').Replace("{InstallDir}", game.InstallDirectory));
+ destination = Environment.ExpandEnvironmentVariables(savePath.Path.Replace('/', '\\').Replace("{InstallDir}", installDirectory));
if (File.Exists(tempSavePathFile))
{
@@ -105,7 +92,7 @@ namespace LANCommander.PlaynitePlugin.Services
if (inInstallDir)
{
// Files are in the game's install directory. Move them there from the save path.
- destination = file.Replace(tempSavePath, savePath.Path.Replace('/', '\\').TrimEnd('\\').Replace("{InstallDir}", game.InstallDirectory));
+ destination = file.Replace(tempSavePath, savePath.Path.Replace('/', '\\').TrimEnd('\\').Replace("{InstallDir}", installDirectory));
if (File.Exists(destination))
File.Delete(destination);
@@ -153,97 +140,89 @@ namespace LANCommander.PlaynitePlugin.Services
}
}
- internal void UploadSave(Game game)
+ public void Upload(string installDirectory)
{
- var manifestPath = Path.Combine(game.InstallDirectory, "_manifest.yml");
+ var manifest = ManifestHelper.Read(installDirectory);
- if (File.Exists(manifestPath))
+ var temp = Path.GetTempFileName();
+
+ if (manifest.SavePaths != null && manifest.SavePaths.Count() > 0)
{
- var deserializer = new DeserializerBuilder()
- .WithNamingConvention(new PascalCaseNamingConvention())
- .Build();
-
- var manifest = deserializer.Deserialize(File.ReadAllText(manifestPath));
- var temp = Path.GetTempFileName();
-
- if (manifest.SavePaths != null && manifest.SavePaths.Count() > 0)
+ using (var archive = ZipArchive.Create())
{
- using (var archive = ZipArchive.Create())
+ archive.DeflateCompressionLevel = SharpCompress.Compressors.Deflate.CompressionLevel.BestCompression;
+
+ #region Add files from defined paths
+ foreach (var savePath in manifest.SavePaths.Where(sp => sp.Type == "File"))
{
- archive.DeflateCompressionLevel = SharpCompress.Compressors.Deflate.CompressionLevel.BestCompression;
+ var localPath = Environment.ExpandEnvironmentVariables(savePath.Path.Replace('/', '\\').Replace("{InstallDir}", installDirectory));
- #region Add files from defined paths
- foreach (var savePath in manifest.SavePaths.Where(sp => sp.Type == "File"))
+ if (Directory.Exists(localPath))
{
- var localPath = Environment.ExpandEnvironmentVariables(savePath.Path.Replace('/', '\\').Replace("{InstallDir}", game.InstallDirectory));
-
- if (Directory.Exists(localPath))
- {
- AddDirectoryToZip(archive, localPath, localPath, savePath.Id);
- }
- else if (File.Exists(localPath))
- {
- archive.AddEntry(Path.Combine(savePath.Id.ToString(), savePath.Path.Replace("{InstallDir}/", "")), localPath);
- }
+ AddDirectoryToZip(archive, localPath, localPath, savePath.Id);
}
- #endregion
-
- #region Add files from defined paths
- foreach (var savePath in manifest.SavePaths.Where(sp => sp.Type == "File"))
+ else if (File.Exists(localPath))
{
- var localPath = Environment.ExpandEnvironmentVariables(savePath.Path.Replace('/', '\\').Replace("{InstallDir}", game.InstallDirectory));
-
- if (Directory.Exists(localPath))
- {
- AddDirectoryToZip(archive, localPath, localPath, savePath.Id);
- }
- else if (File.Exists(localPath))
- {
- archive.AddEntry(Path.Combine(savePath.Id.ToString(), savePath.Path.Replace("{InstallDir}/", "")), localPath);
- }
+ archive.AddEntry(Path.Combine(savePath.Id.ToString(), savePath.Path.Replace("{InstallDir}/", "")), localPath);
}
- #endregion
+ }
+ #endregion
- #region Export registry keys
- if (manifest.SavePaths.Any(sp => sp.Type == "Registry"))
+ #region Add files from defined paths
+ foreach (var savePath in manifest.SavePaths.Where(sp => sp.Type == "File"))
+ {
+ var localPath = Environment.ExpandEnvironmentVariables(savePath.Path.Replace('/', '\\').Replace("{InstallDir}", installDirectory));
+
+ if (Directory.Exists(localPath))
{
- List tempRegFiles = new List();
-
- var exportCommand = new StringBuilder();
-
- foreach (var savePath in manifest.SavePaths.Where(sp => sp.Type == "Registry"))
- {
- var tempRegFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".reg");
-
- exportCommand.AppendLine($"reg.exe export \"{savePath.Path.Replace(":\\", "\\")}\" \"{tempRegFile}\"");
- tempRegFiles.Add(tempRegFile);
- }
-
- PowerShellRuntime.RunCommand(exportCommand.ToString());
-
- var exportFile = new StringBuilder();
-
- foreach (var tempRegFile in tempRegFiles)
- {
- exportFile.AppendLine(File.ReadAllText(tempRegFile));
- File.Delete(tempRegFile);
- }
-
- archive.AddEntry("_registry.reg", new MemoryStream(Encoding.UTF8.GetBytes(exportFile.ToString())), true);
+ AddDirectoryToZip(archive, localPath, localPath, savePath.Id);
}
- #endregion
-
- archive.AddEntry("_manifest.yml", manifestPath);
-
- using (var ms = new MemoryStream())
+ else if (File.Exists(localPath))
{
- archive.SaveTo(ms);
-
- ms.Seek(0, SeekOrigin.Begin);
-
- var save = LANCommander.UploadSave(game.GameId, ms.ToArray());
+ archive.AddEntry(Path.Combine(savePath.Id.ToString(), savePath.Path.Replace("{InstallDir}/", "")), localPath);
}
}
+ #endregion
+
+ #region Export registry keys
+ if (manifest.SavePaths.Any(sp => sp.Type == "Registry"))
+ {
+ List tempRegFiles = new List();
+
+ var exportCommand = new StringBuilder();
+
+ foreach (var savePath in manifest.SavePaths.Where(sp => sp.Type == "Registry"))
+ {
+ var tempRegFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".reg");
+
+ exportCommand.AppendLine($"reg.exe export \"{savePath.Path.Replace(":\\", "\\")}\" \"{tempRegFile}\"");
+ tempRegFiles.Add(tempRegFile);
+ }
+
+ PowerShellRuntime.RunCommand(exportCommand.ToString());
+
+ var exportFile = new StringBuilder();
+
+ foreach (var tempRegFile in tempRegFiles)
+ {
+ exportFile.AppendLine(File.ReadAllText(tempRegFile));
+ File.Delete(tempRegFile);
+ }
+
+ archive.AddEntry("_registry.reg", new MemoryStream(Encoding.UTF8.GetBytes(exportFile.ToString())), true);
+ }
+ #endregion
+
+ archive.AddEntry("_manifest.yml", ManifestHelper.GetPath(installDirectory));
+
+ using (var ms = new MemoryStream())
+ {
+ archive.SaveTo(ms);
+
+ ms.Seek(0, SeekOrigin.Begin);
+
+ var save = Client.UploadSave(manifest.Id.ToString(), ms.ToArray());
+ }
}
}
}
diff --git a/LANCommander.SDK/Helpers/ManifestHelper.cs b/LANCommander.SDK/Helpers/ManifestHelper.cs
index 7e82586..f4e8ed7 100644
--- a/LANCommander.SDK/Helpers/ManifestHelper.cs
+++ b/LANCommander.SDK/Helpers/ManifestHelper.cs
@@ -16,7 +16,7 @@ namespace LANCommander.SDK.Helpers
public static GameManifest Read(string installDirectory)
{
- var source = Path.Combine(installDirectory, ManifestFilename);
+ var source = GetPath(installDirectory);
var yaml = File.ReadAllText(source);
var deserializer = new DeserializerBuilder()
@@ -32,7 +32,7 @@ namespace LANCommander.SDK.Helpers
public static void Write(GameManifest manifest, string installDirectory)
{
- var destination = Path.Combine(installDirectory, ManifestFilename);
+ var destination = GetPath(installDirectory);
Logger.LogTrace("Attempting to write manifest to path {Destination}", destination);
@@ -48,5 +48,10 @@ namespace LANCommander.SDK.Helpers
File.WriteAllText(destination, yaml);
}
+
+ public static string GetPath(string installDirectory)
+ {
+ return Path.Combine(installDirectory, ManifestFilename);
+ }
}
}
diff --git a/LANCommander.SDK/LANCommander.SDK.csproj b/LANCommander.SDK/LANCommander.SDK.csproj
index 11a21d2..7130409 100644
--- a/LANCommander.SDK/LANCommander.SDK.csproj
+++ b/LANCommander.SDK/LANCommander.SDK.csproj
@@ -1,4 +1,4 @@
-
+
netstandard2.0
@@ -8,7 +8,7 @@
-
+