2024-10-04 23:37:49 -05:00
|
|
|
|
using LANCommander.SDK.Extensions;
|
2023-11-10 01:32:30 -06:00
|
|
|
|
using LANCommander.SDK.Helpers;
|
|
|
|
|
|
using LANCommander.SDK.Models;
|
2023-11-16 00:47:01 -06:00
|
|
|
|
using LANCommander.SDK.PowerShell;
|
2023-12-26 18:05:27 -06:00
|
|
|
|
using Microsoft.Extensions.Logging;
|
2023-04-07 19:09:00 -05:00
|
|
|
|
using SharpCompress.Common;
|
|
|
|
|
|
using SharpCompress.Readers;
|
2023-03-29 20:49:31 -05:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Linq;
|
2023-11-30 18:25:37 -06:00
|
|
|
|
using System.Text.RegularExpressions;
|
2023-12-26 18:07:06 -06:00
|
|
|
|
using System.Threading.Tasks;
|
2025-10-06 20:29:34 -05:00
|
|
|
|
using LANCommander.SDK.Abstractions;
|
2025-09-22 00:29:51 -05:00
|
|
|
|
using LANCommander.SDK.Factories;
|
2025-03-18 20:30:04 -05:00
|
|
|
|
using LANCommander.SDK.Utilities;
|
2025-09-22 00:29:51 -05:00
|
|
|
|
using Action = System.Action;
|
2023-03-29 20:49:31 -05:00
|
|
|
|
|
2025-08-18 03:03:33 -05:00
|
|
|
|
// Some terms for this file since they're probably going to be needed in the future:
|
2023-12-23 17:39:37 -06:00
|
|
|
|
// Local path - The full path of the file/directory on the local disk. No variables used, just the raw path for current machine
|
|
|
|
|
|
// Actual path - The path where the entries should be extracted to, before expanding environemnt variables.
|
|
|
|
|
|
// Archive path - The path of where the entries are located in the ZIP
|
|
|
|
|
|
//
|
|
|
|
|
|
// Other notes:
|
|
|
|
|
|
// - Entries in the ZIP are separated by save path ID to avoid collision
|
|
|
|
|
|
|
2024-10-04 23:37:49 -05:00
|
|
|
|
namespace LANCommander.SDK.Services
|
2023-03-29 20:49:31 -05:00
|
|
|
|
{
|
2025-09-24 20:58:23 -05:00
|
|
|
|
public class SaveClient(
|
2025-09-22 00:29:51 -05:00
|
|
|
|
ApiRequestFactory apiRequestFactory,
|
2025-10-06 20:29:34 -05:00
|
|
|
|
ISettingsProvider settingsProvider,
|
2025-11-26 01:09:52 -06:00
|
|
|
|
PowerShellScriptFactory powerShellScriptFactory,
|
2025-09-24 20:58:23 -05:00
|
|
|
|
ILogger<SaveClient> logger)
|
2023-03-29 20:49:31 -05:00
|
|
|
|
{
|
2023-11-10 01:32:30 -06:00
|
|
|
|
public delegate void OnDownloadProgressHandler(DownloadProgressChangedEventArgs e);
|
|
|
|
|
|
public event OnDownloadProgressHandler OnDownloadProgress;
|
|
|
|
|
|
|
2025-09-22 00:29:51 -05:00
|
|
|
|
public delegate void OnDownloadCompleteHandler();
|
2023-11-10 01:32:30 -06:00
|
|
|
|
public event OnDownloadCompleteHandler OnDownloadComplete;
|
|
|
|
|
|
|
2025-09-22 00:29:51 -05:00
|
|
|
|
private async Task<FileInfo> DownloadAsync(Guid id, Action<DownloadProgressChangedEventArgs> progressHandler, Action completeHandler)
|
2023-12-26 18:05:27 -06:00
|
|
|
|
{
|
2025-09-22 00:29:51 -05:00
|
|
|
|
var destination = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
|
|
|
|
|
|
|
|
|
|
|
return await apiRequestFactory
|
|
|
|
|
|
.Create()
|
|
|
|
|
|
.UseAuthenticationToken()
|
|
|
|
|
|
.UseVersioning()
|
|
|
|
|
|
.UseRoute($"/api/Saves/{id}/Download")
|
|
|
|
|
|
.OnProgress(progressHandler)
|
|
|
|
|
|
.OnComplete(completeHandler)
|
|
|
|
|
|
.DownloadAsync(destination);
|
2023-12-26 18:05:27 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-22 00:29:51 -05:00
|
|
|
|
public async Task<FileInfo> DownloadLatestAsync(Guid gameId, Action<DownloadProgressChangedEventArgs> progressHandler, Action completeHandler)
|
2024-01-02 02:34:58 -06:00
|
|
|
|
{
|
2025-09-22 00:29:51 -05:00
|
|
|
|
var destination = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
|
|
|
|
|
|
|
|
|
|
|
return await apiRequestFactory
|
|
|
|
|
|
.Create()
|
|
|
|
|
|
.UseAuthenticationToken()
|
|
|
|
|
|
.UseVersioning()
|
|
|
|
|
|
.UseRoute($"/api/Saves/Game/{gameId}/Latest/Download")
|
|
|
|
|
|
.OnProgress(progressHandler)
|
|
|
|
|
|
.OnComplete(completeHandler)
|
|
|
|
|
|
.DownloadAsync(destination);
|
2024-02-17 17:30:14 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-23 18:17:01 -05:00
|
|
|
|
public async Task<IEnumerable<GameSave>> GetAsync(Guid gameId)
|
|
|
|
|
|
{
|
2025-09-22 00:29:51 -05:00
|
|
|
|
return await apiRequestFactory
|
|
|
|
|
|
.Create()
|
|
|
|
|
|
.UseAuthenticationToken()
|
|
|
|
|
|
.UseVersioning()
|
|
|
|
|
|
.UseRoute($"/api/Saves/Game/{gameId}")
|
|
|
|
|
|
.GetAsync<IEnumerable<GameSave>>();
|
2024-02-06 19:36:55 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-22 00:29:51 -05:00
|
|
|
|
public async Task<GameSave> GetLatestAsync(Guid gameId)
|
2024-07-15 18:17:04 -05:00
|
|
|
|
{
|
2025-09-22 00:29:51 -05:00
|
|
|
|
return await apiRequestFactory
|
|
|
|
|
|
.Create()
|
|
|
|
|
|
.UseAuthenticationToken()
|
|
|
|
|
|
.UseVersioning()
|
|
|
|
|
|
.UseRoute($"/api/Saves/Game/{gameId}/Latest")
|
|
|
|
|
|
.GetAsync<GameSave>();
|
2024-07-15 18:17:04 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-07 01:14:13 -05:00
|
|
|
|
public async Task DownloadAsync(string installDirectory, Guid gameId, Guid? saveId = null)
|
2023-03-29 20:49:31 -05:00
|
|
|
|
{
|
2025-11-29 18:24:27 -06:00
|
|
|
|
var manifest = await ManifestHelper.ReadAsync<SDK.Models.Manifest.Game>(installDirectory, gameId);
|
2023-11-10 01:32:30 -06:00
|
|
|
|
|
2025-08-18 03:03:28 -05:00
|
|
|
|
string tempFile;
|
2024-10-04 23:37:49 -05:00
|
|
|
|
string tempLocation = string.Empty;
|
2023-03-29 20:49:31 -05:00
|
|
|
|
|
2023-11-10 01:32:30 -06:00
|
|
|
|
if (manifest != null)
|
2023-03-29 20:49:31 -05:00
|
|
|
|
{
|
2025-09-22 00:29:51 -05:00
|
|
|
|
FileInfo destination;
|
2024-02-17 17:30:14 -06:00
|
|
|
|
|
|
|
|
|
|
if (!saveId.HasValue)
|
2023-03-29 20:49:31 -05:00
|
|
|
|
{
|
2024-08-07 01:14:13 -05:00
|
|
|
|
destination = await DownloadLatestAsync(manifest.Id, (changed) =>
|
2024-02-17 17:30:14 -06:00
|
|
|
|
{
|
|
|
|
|
|
OnDownloadProgress?.Invoke(changed);
|
2025-09-22 00:29:51 -05:00
|
|
|
|
}, () =>
|
2024-02-17 17:30:14 -06:00
|
|
|
|
{
|
2025-09-22 00:29:51 -05:00
|
|
|
|
OnDownloadComplete?.Invoke();
|
2024-02-17 17:30:14 -06:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
2023-03-29 20:49:31 -05:00
|
|
|
|
{
|
2024-08-07 01:14:13 -05:00
|
|
|
|
destination = await DownloadAsync(saveId.Value, (changed) =>
|
2024-02-17 17:30:14 -06:00
|
|
|
|
{
|
|
|
|
|
|
OnDownloadProgress?.Invoke(changed);
|
2025-09-22 00:29:51 -05:00
|
|
|
|
}, () =>
|
2024-02-17 17:30:14 -06:00
|
|
|
|
{
|
2025-09-22 00:29:51 -05:00
|
|
|
|
OnDownloadComplete?.Invoke();
|
2024-02-17 17:30:14 -06:00
|
|
|
|
});
|
|
|
|
|
|
}
|
2025-09-22 00:29:51 -05:00
|
|
|
|
|
|
|
|
|
|
if (!destination.Exists)
|
2023-12-27 17:22:36 -06:00
|
|
|
|
return;
|
|
|
|
|
|
|
2025-09-22 00:29:51 -05:00
|
|
|
|
logger?.LogTrace("Game save archive downloaded to {SaveTempLocation}", destination);
|
2023-12-26 18:07:06 -06:00
|
|
|
|
|
2025-09-22 00:29:51 -05:00
|
|
|
|
tempFile = destination.FullName;
|
2023-11-10 01:32:30 -06:00
|
|
|
|
|
2023-03-29 20:49:31 -05:00
|
|
|
|
// Go into the archive and extract the files to the correct locations
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2023-12-26 18:07:06 -06:00
|
|
|
|
tempLocation = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
2023-03-29 20:49:31 -05:00
|
|
|
|
|
|
|
|
|
|
Directory.CreateDirectory(tempLocation);
|
|
|
|
|
|
|
2024-10-04 23:37:49 -05:00
|
|
|
|
bool success = RetryHelper.RetryOnException(10, TimeSpan.FromMilliseconds(200), false, () =>
|
2023-12-26 18:07:06 -06:00
|
|
|
|
{
|
2025-09-22 00:29:51 -05:00
|
|
|
|
logger?.LogTrace("Attempting to extracting save entries to the temporary location {TempPath}", tempLocation);
|
2023-12-26 18:07:06 -06:00
|
|
|
|
|
|
|
|
|
|
ExtractFilesFromZip(tempFile, tempLocation);
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (!success)
|
|
|
|
|
|
throw new ExtractionException("Could not extract the save archive. Is the file locked?");
|
|
|
|
|
|
|
2025-11-29 18:24:27 -06:00
|
|
|
|
manifest = await ManifestHelper.ReadAsync<SDK.Models.Manifest.Game>(tempLocation);
|
2023-03-29 20:49:31 -05:00
|
|
|
|
|
|
|
|
|
|
#region Move files
|
2024-03-28 19:59:15 -05:00
|
|
|
|
var tempLocationFilePath = "Files";
|
|
|
|
|
|
|
|
|
|
|
|
// Legacy support
|
|
|
|
|
|
if (!Directory.Exists(Path.Combine(tempLocation, tempLocationFilePath)))
|
|
|
|
|
|
tempLocationFilePath = "Saves";
|
|
|
|
|
|
|
2024-08-26 18:37:57 -05:00
|
|
|
|
foreach (var savePath in manifest.SavePaths.Where(sp => sp.Type == Enums.SavePathType.File))
|
2023-03-29 20:49:31 -05:00
|
|
|
|
{
|
2025-09-22 00:29:51 -05:00
|
|
|
|
var entries = GetFileSavePathEntries(savePath, installDirectory) ?? [];
|
2025-05-24 18:50:51 +02:00
|
|
|
|
|
|
|
|
|
|
foreach (var entry in entries)
|
2023-03-29 20:49:31 -05:00
|
|
|
|
{
|
2024-03-28 19:59:15 -05:00
|
|
|
|
var entryPath = Path.Combine(tempLocation, tempLocationFilePath, savePath.Id.ToString(), entry.ArchivePath.Replace('/', Path.DirectorySeparatorChar));
|
2023-12-26 18:07:06 -06:00
|
|
|
|
var destinationPath = entry.ActualPath.ExpandEnvironmentVariables(installDirectory);
|
2023-03-29 20:49:31 -05:00
|
|
|
|
|
2023-12-26 18:07:06 -06:00
|
|
|
|
if (File.Exists(entryPath))
|
2023-04-02 20:50:57 -05:00
|
|
|
|
{
|
2025-05-24 18:50:51 +02:00
|
|
|
|
var destinationDirectory = Path.GetDirectoryName(destinationPath);
|
2024-10-15 02:00:54 -05:00
|
|
|
|
|
|
|
|
|
|
Directory.CreateDirectory(destinationDirectory);
|
|
|
|
|
|
|
2023-12-26 18:07:06 -06:00
|
|
|
|
// Handle individual files that were saved as an entry in the path
|
|
|
|
|
|
if (File.Exists(destinationPath))
|
|
|
|
|
|
File.Delete(destinationPath);
|
2023-11-30 18:30:15 -06:00
|
|
|
|
|
2023-12-26 18:07:06 -06:00
|
|
|
|
File.Move(entryPath, destinationPath);
|
2023-11-30 18:37:23 -06:00
|
|
|
|
}
|
2023-12-26 18:07:06 -06:00
|
|
|
|
else if (Directory.Exists(entryPath))
|
2023-11-30 18:37:23 -06:00
|
|
|
|
{
|
2023-12-26 18:07:06 -06:00
|
|
|
|
// Handle directories that were saved as an entry in the path
|
|
|
|
|
|
var entryFiles = Directory.GetFiles(entryPath, "*", SearchOption.AllDirectories);
|
2023-11-30 18:30:15 -06:00
|
|
|
|
|
2023-12-26 18:07:06 -06:00
|
|
|
|
foreach (var entryFile in entryFiles)
|
2023-11-30 18:30:15 -06:00
|
|
|
|
{
|
2025-05-24 18:50:51 +02:00
|
|
|
|
var fileDestination = entryFile.Replace(entryPath, destinationPath);
|
|
|
|
|
|
|
|
|
|
|
|
var destinationDirectory = Path.GetDirectoryName(fileDestination);
|
2024-10-15 02:00:54 -05:00
|
|
|
|
|
|
|
|
|
|
Directory.CreateDirectory(destinationDirectory);
|
|
|
|
|
|
|
2023-12-26 18:07:06 -06:00
|
|
|
|
if (File.Exists(fileDestination))
|
|
|
|
|
|
File.Delete(fileDestination);
|
|
|
|
|
|
|
|
|
|
|
|
File.Move(entryFile, fileDestination);
|
2023-11-30 18:30:15 -06:00
|
|
|
|
}
|
2023-04-02 20:50:57 -05:00
|
|
|
|
}
|
2023-03-29 20:49:31 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region Handle registry importing
|
2025-06-28 00:10:54 +02:00
|
|
|
|
var registryImportFilePaths = Directory.GetFiles(tempLocation, "_registry*.reg");
|
|
|
|
|
|
var importer = new RegistryImportUtility();
|
2023-03-29 20:49:31 -05:00
|
|
|
|
|
2025-06-28 00:10:54 +02:00
|
|
|
|
foreach (var registryImportFilePath in registryImportFilePaths)
|
2023-03-29 20:49:31 -05:00
|
|
|
|
{
|
|
|
|
|
|
var registryImportFileContents = File.ReadAllText(registryImportFilePath);
|
|
|
|
|
|
|
2025-11-26 01:09:52 -06:00
|
|
|
|
var script = powerShellScriptFactory.Create(Enums.ScriptType.SaveDownload);
|
2023-11-16 00:47:01 -06:00
|
|
|
|
|
2025-06-27 23:57:03 +02:00
|
|
|
|
string adminArgument = string.Empty;
|
2023-11-16 00:47:01 -06:00
|
|
|
|
if (registryImportFileContents.Contains("HKEY_LOCAL_MACHINE"))
|
2025-06-27 23:57:03 +02:00
|
|
|
|
{
|
2024-08-08 18:00:41 -05:00
|
|
|
|
script.AsAdmin();
|
2025-06-27 23:57:03 +02:00
|
|
|
|
adminArgument = " -Verb RunAs";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
script.UseInline($"Start-Process regedit.exe {adminArgument} -ArgumentList \"/s\", \"{registryImportFilePath}\"");
|
2023-11-16 00:47:01 -06:00
|
|
|
|
|
2025-10-06 20:29:34 -05:00
|
|
|
|
if (settingsProvider.CurrentValue.Debug.EnableScriptDebugging)
|
2025-06-27 23:20:10 +02:00
|
|
|
|
{
|
2024-08-16 18:36:17 -05:00
|
|
|
|
script.EnableDebug();
|
2025-06-27 23:20:10 +02:00
|
|
|
|
}
|
2024-08-16 18:36:17 -05:00
|
|
|
|
|
2024-09-20 00:27:20 -05:00
|
|
|
|
await script.ExecuteAsync<int>();
|
2023-03-29 20:49:31 -05:00
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
// Clean up temp files
|
|
|
|
|
|
Directory.Delete(tempLocation, true);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2025-09-22 00:29:51 -05:00
|
|
|
|
logger?.LogError(ex, "The files in a save could not be extracted to their destination");
|
2023-12-26 18:07:06 -06:00
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Directory.Exists(tempLocation))
|
|
|
|
|
|
Directory.Delete(tempLocation, true);
|
2023-03-29 20:49:31 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-29 18:24:27 -06:00
|
|
|
|
public async Task<Stream> PackAsync(string installDirectory, SDK.Models.Manifest.Game manifest)
|
2023-03-29 20:49:31 -05:00
|
|
|
|
{
|
2025-03-18 20:30:04 -05:00
|
|
|
|
using (var savePacker = new SavePacker(installDirectory))
|
2023-11-10 01:32:30 -06:00
|
|
|
|
{
|
2025-03-18 20:30:04 -05:00
|
|
|
|
if (manifest?.SavePaths.Any() ?? false)
|
|
|
|
|
|
savePacker.AddPaths(manifest.SavePaths);
|
2023-03-29 20:49:31 -05:00
|
|
|
|
|
2025-03-18 20:30:04 -05:00
|
|
|
|
await savePacker.AddManifestAsync(manifest);
|
|
|
|
|
|
|
|
|
|
|
|
return await savePacker.PackAsync();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-03-29 20:49:31 -05:00
|
|
|
|
|
2025-11-29 18:24:27 -06:00
|
|
|
|
public async Task<GameSave> UploadAsync(Stream stream, SDK.Models.Manifest.Game manifest)
|
2025-03-19 17:35:16 -05:00
|
|
|
|
{
|
2025-09-22 00:29:51 -05:00
|
|
|
|
return await apiRequestFactory
|
|
|
|
|
|
.Create()
|
|
|
|
|
|
.UseAuthenticationToken()
|
|
|
|
|
|
.UseVersioning()
|
2025-12-04 14:38:09 +11:00
|
|
|
|
.UseRoute($"/api/Saves/Game/{manifest.Id}/Upload")
|
2025-12-04 13:35:05 +11:00
|
|
|
|
.UploadAsync<GameSave>($"game-{manifest.Id}-save", stream);
|
2025-03-19 17:35:16 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-18 03:03:28 -05:00
|
|
|
|
public async Task UploadAsync(string installDirectory, Guid gameId)
|
2025-03-18 20:30:04 -05:00
|
|
|
|
{
|
|
|
|
|
|
using (var savePacker = new SavePacker(installDirectory))
|
|
|
|
|
|
{
|
2025-11-29 18:24:27 -06:00
|
|
|
|
var manifest = await ManifestHelper.ReadAsync<SDK.Models.Manifest.Game>(installDirectory, gameId);
|
2023-11-30 18:25:37 -06:00
|
|
|
|
|
2025-06-25 23:25:26 +02:00
|
|
|
|
if (manifest?.SavePaths?.Any() ?? false)
|
2025-03-18 20:30:04 -05:00
|
|
|
|
savePacker.AddPaths(manifest.SavePaths);
|
2023-03-29 20:49:31 -05:00
|
|
|
|
|
2025-06-25 23:25:26 +02:00
|
|
|
|
if (savePacker.HasEntries())
|
|
|
|
|
|
{
|
|
|
|
|
|
await savePacker.AddManifestAsync(manifest);
|
|
|
|
|
|
|
|
|
|
|
|
var stream = await savePacker.PackAsync();
|
|
|
|
|
|
|
2025-08-18 03:03:28 -05:00
|
|
|
|
await UploadAsync(stream, manifest);
|
2025-06-25 23:25:26 +02:00
|
|
|
|
}
|
2023-03-29 20:49:31 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-23 18:17:01 -05:00
|
|
|
|
public async Task DeleteAsync(Guid id)
|
2024-02-17 17:30:14 -06:00
|
|
|
|
{
|
2025-09-22 00:29:51 -05:00
|
|
|
|
await apiRequestFactory
|
|
|
|
|
|
.Create()
|
|
|
|
|
|
.UseAuthenticationToken()
|
|
|
|
|
|
.UseVersioning()
|
|
|
|
|
|
.UseRoute($"/api/Saves/{id}")
|
|
|
|
|
|
.DeleteAsync<bool>();
|
2024-02-17 17:30:14 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-29 18:24:27 -06:00
|
|
|
|
public IEnumerable<SavePathEntry> GetFileSavePathEntries(SDK.Models.Manifest.SavePath savePath, string installDirectory)
|
2023-12-23 02:00:53 -06:00
|
|
|
|
{
|
|
|
|
|
|
IEnumerable<string> localPaths;
|
|
|
|
|
|
|
|
|
|
|
|
if (savePath.IsRegex)
|
|
|
|
|
|
{
|
|
|
|
|
|
var workingDirectory = GetLocalPath(savePath.WorkingDirectory, installDirectory);
|
|
|
|
|
|
var pattern = savePath.Path;
|
|
|
|
|
|
|
2024-10-04 23:37:49 -05:00
|
|
|
|
if (string.IsNullOrWhiteSpace(workingDirectory))
|
2023-12-23 02:00:53 -06:00
|
|
|
|
workingDirectory = installDirectory;
|
|
|
|
|
|
|
|
|
|
|
|
if (Path.DirectorySeparatorChar == '\\')
|
|
|
|
|
|
{
|
|
|
|
|
|
pattern = pattern.Replace("\\", "\\\\");
|
|
|
|
|
|
pattern = pattern.Replace("/", "\\\\");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var regex = new Regex(pattern);
|
|
|
|
|
|
|
|
|
|
|
|
localPaths = Directory.GetFiles(workingDirectory, "*", SearchOption.AllDirectories)
|
|
|
|
|
|
.Where(p => regex.IsMatch(p.Substring(workingDirectory.Length).TrimStart(Path.DirectorySeparatorChar)))
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
var workingDirectory = GetLocalPath(savePath.WorkingDirectory, installDirectory);
|
|
|
|
|
|
|
|
|
|
|
|
var localPath = Path.Combine(workingDirectory, GetLocalPath(savePath.Path, installDirectory));
|
|
|
|
|
|
|
2025-08-18 03:03:28 -05:00
|
|
|
|
localPaths = new[] { localPath };
|
2023-12-23 02:00:53 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var entries = new List<SavePathEntry>();
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var localPath in localPaths)
|
|
|
|
|
|
{
|
|
|
|
|
|
var actualPath = localPath.DeflateEnvironmentVariables(installDirectory);
|
|
|
|
|
|
var workingDirectory = savePath.WorkingDirectory.DeflateEnvironmentVariables(installDirectory);
|
|
|
|
|
|
var archivePath = actualPath.Replace(workingDirectory, "").TrimStart(Path.DirectorySeparatorChar);
|
|
|
|
|
|
|
|
|
|
|
|
entries.Add(new SavePathEntry
|
|
|
|
|
|
{
|
|
|
|
|
|
ArchivePath = archivePath.Replace(Path.DirectorySeparatorChar, '/'),
|
|
|
|
|
|
ActualPath = actualPath.Replace(Path.DirectorySeparatorChar, '/')
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
savePath.Entries = entries;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return entries;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-23 00:11:16 -06:00
|
|
|
|
public string GetLocalPath(string path, string installDirectory)
|
|
|
|
|
|
{
|
2023-12-23 17:39:37 -06:00
|
|
|
|
var localPath = path.ExpandEnvironmentVariables(installDirectory);
|
2023-12-23 00:11:16 -06:00
|
|
|
|
|
|
|
|
|
|
if (Path.DirectorySeparatorChar == '/')
|
|
|
|
|
|
localPath = localPath.Replace('\\', Path.DirectorySeparatorChar);
|
|
|
|
|
|
else
|
|
|
|
|
|
localPath = localPath.Replace('/', Path.DirectorySeparatorChar);
|
|
|
|
|
|
|
|
|
|
|
|
return localPath;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-23 17:39:37 -06:00
|
|
|
|
public string GetActualPath(string path, string installDirectory)
|
2023-12-23 00:11:16 -06:00
|
|
|
|
{
|
2023-12-23 17:39:37 -06:00
|
|
|
|
var actualPath = path.DeflateEnvironmentVariables(installDirectory);
|
2023-12-23 00:11:16 -06:00
|
|
|
|
|
|
|
|
|
|
if (Path.DirectorySeparatorChar == '\\')
|
2025-08-18 03:03:06 -05:00
|
|
|
|
actualPath = path.Replace('/', Path.DirectorySeparatorChar);
|
2023-12-23 00:11:16 -06:00
|
|
|
|
|
2023-12-23 17:39:37 -06:00
|
|
|
|
return actualPath;
|
2023-12-23 00:11:16 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-23 17:39:37 -06:00
|
|
|
|
public string GetArchivePath(string path, string workingDirectory, string installDirectory)
|
2023-03-29 20:49:31 -05:00
|
|
|
|
{
|
2023-12-23 17:39:37 -06:00
|
|
|
|
path = GetLocalPath(path, installDirectory);
|
|
|
|
|
|
workingDirectory = GetLocalPath(workingDirectory, installDirectory);
|
2023-03-29 20:49:31 -05:00
|
|
|
|
|
2023-12-23 17:39:37 -06:00
|
|
|
|
var archivePath = path.Replace(workingDirectory, "").Trim(Path.DirectorySeparatorChar);
|
2023-03-29 20:49:31 -05:00
|
|
|
|
|
2023-12-23 17:39:37 -06:00
|
|
|
|
if (Path.DirectorySeparatorChar == '\\')
|
|
|
|
|
|
archivePath = archivePath.Replace(Path.DirectorySeparatorChar, '/');
|
|
|
|
|
|
|
|
|
|
|
|
return archivePath;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-03-29 20:49:31 -05:00
|
|
|
|
private void ExtractFilesFromZip(string zipPath, string destination)
|
|
|
|
|
|
{
|
2023-04-07 19:09:00 -05:00
|
|
|
|
using (var fs = File.OpenRead(zipPath))
|
2025-10-09 02:16:20 -05:00
|
|
|
|
using (var ts = new TrackableStream(fs, fs.Length))
|
2023-04-07 19:09:00 -05:00
|
|
|
|
using (var reader = ReaderFactory.Open(ts))
|
2023-03-29 20:49:31 -05:00
|
|
|
|
{
|
2023-04-07 19:09:00 -05:00
|
|
|
|
reader.WriteAllToDirectory(destination, new ExtractionOptions()
|
2023-03-29 20:49:31 -05:00
|
|
|
|
{
|
2023-04-07 19:09:00 -05:00
|
|
|
|
ExtractFullPath = true,
|
|
|
|
|
|
Overwrite = true
|
|
|
|
|
|
});
|
2023-03-29 20:49:31 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|