From aa8aba154ed3701c76d2daa33e9ae1929d9bdcf6 Mon Sep 17 00:00:00 2001 From: Pat Hartl Date: Tue, 28 Mar 2023 23:10:18 -0500 Subject: [PATCH] Basic packing/extraction of saves. Files only, no directories or registry entries. --- .../LANCommanderLibraryPlugin.cs | 159 +++++++++++++++++- 1 file changed, 150 insertions(+), 9 deletions(-) diff --git a/LANCommander.Playnite.Extension/LANCommanderLibraryPlugin.cs b/LANCommander.Playnite.Extension/LANCommanderLibraryPlugin.cs index e1eae71..3fce6e5 100644 --- a/LANCommander.Playnite.Extension/LANCommanderLibraryPlugin.cs +++ b/LANCommander.Playnite.Extension/LANCommanderLibraryPlugin.cs @@ -1,4 +1,5 @@ -using ICSharpCode.SharpZipLib.Zip; +using ICSharpCode.SharpZipLib.Core; +using ICSharpCode.SharpZipLib.Zip; using LANCommander.PlaynitePlugin.Extensions; using LANCommander.SDK; using Playnite.SDK; @@ -6,6 +7,7 @@ using Playnite.SDK.Events; using Playnite.SDK.Models; using Playnite.SDK.Plugins; using System; +using System.CodeDom.Compiler; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; @@ -267,9 +269,99 @@ namespace LANCommander.PlaynitePlugin }; } + public override void OnGameStarting(OnGameStartingEventArgs args) + { + DownloadSave(args.Game); + } + + private void DownloadSave(Game game) + { + string tempFile = String.Empty; + + if (game != null) + { + PlayniteApi.Dialogs.ActivateGlobalProgress(progress => + { + 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...") + { + IsIndeterminate = false, + Cancelable = false + }); + + // Go into the archive and extract the files to the correct locations + try + { + var tempLocation = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + + Directory.CreateDirectory(tempLocation); + + ExtractFilesFromZip(tempFile, tempLocation); + + var deserializer = new DeserializerBuilder() + .WithNamingConvention(new PascalCaseNamingConvention()) + .Build(); + + var manifestContents = File.ReadAllText(Path.Combine(tempLocation, "_manifest.yml")); + + var manifest = deserializer.Deserialize(manifestContents); + + foreach (var savePath in manifest.SavePaths) + { + var pathTemp = Path.Combine(tempLocation, savePath.Id.ToString(), savePath.Path.Replace('/', '\\').Replace("{InstallDir}\\", "")); + var destination = savePath.Path.Replace('/', '\\').Replace("{InstallDir}", game.InstallDirectory); + + if (File.Exists(pathTemp)) + { + if (File.Exists(destination)) + File.Delete(destination); + + File.Move(pathTemp, destination); + } + else if (Directory.Exists(pathTemp)) + { + // Better way to handle this? Maybe merge files? + Directory.Delete(destination, true); + Directory.Move(pathTemp, destination); + } + } + + // Clean up temp files + Directory.Delete(tempLocation, true); + } + catch (Exception ex) + { + + } + } + } + public override void OnGameStopped(OnGameStoppedEventArgs args) { - var manifestPath = Path.Combine(args.Game.InstallDirectory, "_manifest.yml"); + UploadSave(args.Game); + } + + private void UploadSave(Game game) + { + var manifestPath = Path.Combine(game.InstallDirectory, "_manifest.yml"); if (File.Exists(manifestPath)) { @@ -286,27 +378,36 @@ namespace LANCommander.PlaynitePlugin foreach (var savePath in manifest.SavePaths) { - savePath.Path = savePath.Path.Replace('/', '\\').Replace("{InstallDir}", args.Game.InstallDirectory); + var localPath = savePath.Path.Replace('/', '\\').Replace("{InstallDir}", game.InstallDirectory); - if (Directory.Exists(savePath.Path)) + if (Directory.Exists(localPath)) { - AddDirectoryToZip(zipStream, savePath.Path); + AddDirectoryToZip(zipStream, localPath); } - else if (File.Exists(savePath.Path)) + else if (File.Exists(localPath)) { - var entry = new ZipEntry(Path.Combine(savePath.Id.ToString(), Path.GetFileName(savePath.Path))); + var entry = new ZipEntry(Path.Combine(savePath.Id.ToString(), savePath.Path.Replace("{InstallDir}/", ""))); zipStream.PutNextEntry(entry); - byte[] buffer = File.ReadAllBytes(savePath.Path); + byte[] buffer = File.ReadAllBytes(localPath); zipStream.Write(buffer, 0, buffer.Length); zipStream.CloseEntry(); } } + + var manifestEntry = new ZipEntry("_manifest.yml"); + + zipStream.PutNextEntry(manifestEntry); + + byte[] manifestBuffer = File.ReadAllBytes(manifestPath); + + zipStream.Write(manifestBuffer, 0, manifestBuffer.Length); + zipStream.CloseEntry(); } - var save = LANCommander.UploadSave(args.Game.GameId, File.ReadAllBytes(temp)); + var save = LANCommander.UploadSave(game.GameId, File.ReadAllBytes(temp)); File.Delete(temp); } @@ -338,6 +439,46 @@ namespace LANCommander.PlaynitePlugin } } + private void ExtractFilesFromZip(string zipPath, string destination) + { + ZipFile file = null; + + try + { + FileStream fs = File.OpenRead(zipPath); + + file = new ZipFile(fs); + + foreach (ZipEntry entry in file) + { + if (!entry.IsFile) + continue; + + byte[] buffer = new byte[4096]; + var zipStream = file.GetInputStream(entry); + + var entryDestination = Path.Combine(destination, entry.Name); + var entryDirectory = Path.GetDirectoryName(entryDestination); + + if (!String.IsNullOrWhiteSpace(entryDirectory)) + Directory.CreateDirectory(entryDirectory); + + using (FileStream streamWriter = File.Create(entryDestination)) + { + StreamUtils.Copy(zipStream, streamWriter, buffer); + } + } + } + finally + { + if (file != null) + { + file.IsStreamOwner = true; + file.Close(); + } + } + } + public override IEnumerable GetTopPanelItems() { yield return new TopPanelItem