/// <summary> /// Purges the mod directory asynchronous. /// </summary> /// <param name="parameters">The parameters.</param> /// <param name="deleteAll">if set to <c>true</c> [delete all].</param> /// <returns>Task<System.Boolean>.</returns> public Task <bool> PurgeModDirectoryAsync(ModWriterParameters parameters, bool deleteAll = false) { var fullPath = Path.Combine(parameters.RootDirectory ?? string.Empty, parameters.Path ?? string.Empty); if (Directory.Exists(fullPath)) { if (!deleteAll) { var files = Directory.EnumerateFiles(fullPath, "*", SearchOption.TopDirectoryOnly); foreach (var item in files) { File.Delete(item); } } else { Directory.Delete(fullPath, true); } return(Task.FromResult(true)); } else if (File.Exists(fullPath)) { File.Delete(fullPath); return(Task.FromResult(true)); } return(Task.FromResult(false)); }
/// <summary> /// Ensures the database exists. /// </summary> /// <param name="parameters">The parameters.</param> private void EnsureDbExists(ModWriterParameters parameters) { var db = GetDbPath(parameters); if (!File.Exists(db)) { File.Copy(Constants.Empty_sql_db_path, db); } }
/// <summary> /// export as an asynchronous operation. /// </summary> /// <param name="parameters">The parameters.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> public async Task <bool> ExportAsync(ModWriterParameters parameters) { EnsureDbExists(parameters); var collection = await RecreateCollectionAsync(parameters); await SyncModsAsync(collection, parameters); return(true); }
/// <summary> /// Mods the directory exists asynchronous. /// </summary> /// <param name="parameters">The parameters.</param> /// <returns>Task<System.Boolean>.</returns> public virtual Task <bool> ModDirectoryExistsAsync(ModWriterParameters parameters) { var fullPath = Path.Combine(parameters.RootDirectory, parameters.Path); if (!Directory.Exists(fullPath)) { return(Task.FromResult(false)); } return(Task.FromResult(Directory.EnumerateFileSystemEntries(fullPath).Any())); }
/// <summary> /// Descriptors the exists asynchronous. /// </summary> /// <param name="parameters">The parameters.</param> /// <returns>Task<System.Boolean>.</returns> public Task <bool> DescriptorExistsAsync(ModWriterParameters parameters) { var fullPath = Path.Combine(parameters.RootDirectory, parameters.Mod.DescriptorFile); if (File.Exists(fullPath)) { return(Task.FromResult(true)); } return(Task.FromResult(false)); }
/// <summary> /// Exports the mods asynchronous. /// </summary> /// <param name="enabledMods">The mods.</param> /// <param name="regularMods">The regular mods.</param> /// <param name="modCollection">The mod collection.</param> /// <returns>Task<System.Boolean>.</returns> public virtual async Task <bool> ExportModsAsync(IReadOnlyCollection <IMod> enabledMods, IReadOnlyCollection <IMod> regularMods, IModCollection modCollection) { var game = GameService.GetSelected(); if (game == null || enabledMods == null || regularMods == null || modCollection == null) { return(false); } var allMods = GetInstalledModsInternal(game, false); var mod = GeneratePatchModDescriptor(allMods, game, GenerateCollectionPatchName(modCollection.Name)); var applyModParams = new ModWriterParameters() { OtherMods = regularMods.Where(p => !enabledMods.Any(m => m.DescriptorFile.Equals(p.DescriptorFile))).ToList(), EnabledMods = enabledMods, RootDirectory = game.UserDirectory }; if (await ModWriter.ModDirectoryExistsAsync(new ModWriterParameters() { RootDirectory = mod.FullPath })) { if (modCollection.PatchModEnabled && enabledMods.Any()) { if (await ModWriter.WriteDescriptorAsync(new ModWriterParameters() { Mod = mod, RootDirectory = game.UserDirectory, Path = mod.DescriptorFile, LockDescriptor = CheckIfModShouldBeLocked(game, mod) }, IsPatchModInternal(mod))) { applyModParams.TopPriorityMods = new List <IMod>() { mod }; Cache.Invalidate(new CacheInvalidateParameters() { Region = ModsCacheRegion, Prefix = game.Type, Keys = new List <string> { GetModsCacheKey(true), GetModsCacheKey(false) } }); } } } else { // Remove left over descriptor if (allMods.Any(p => p.Name.Equals(mod.Name))) { await DeleteDescriptorsInternalAsync(new List <IMod>() { mod }); } } return(await ModWriter.ApplyModsAsync(applyModParams)); }
/// <summary> /// Creates the mod directory asynchronous. /// </summary> /// <param name="parameters">The parameters.</param> /// <returns>Task<System.Boolean>.</returns> public Task <bool> CreateModDirectoryAsync(ModWriterParameters parameters) { var fullPath = Path.Combine(parameters.RootDirectory, parameters.Path); if (!Directory.Exists(fullPath)) { Directory.CreateDirectory(fullPath); return(Task.FromResult(true)); } return(Task.FromResult(false)); }
/// <summary> /// write descriptor as an asynchronous operation. /// </summary> /// <param name="parameters">The parameters.</param> /// <param name="writeDescriptorInModDirectory">if set to <c>true</c> [write descriptor in mod directory].</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> public async Task <bool> WriteDescriptorAsync(ModWriterParameters parameters, bool writeDescriptorInModDirectory) { async Task <bool> writeDescriptors() { // If needed I've got a much more complex serializer, it is written for Kerbal Space Program but the structure seems to be the same though this is much more simpler var fullPath = Path.Combine(parameters.RootDirectory ?? string.Empty, parameters.Path ?? string.Empty); await writeDescriptor(fullPath); // Attempt to fix issues where the game decides to delete local zipped mod descriptors (I'm assuming this happens to all pdx games) if (parameters.LockDescriptor) { if (File.Exists(fullPath)) { _ = new System.IO.FileInfo(fullPath) { IsReadOnly = true }; } } if (writeDescriptorInModDirectory) { var modPath = Path.Combine(parameters.Mod.FileName, Shared.Constants.DescriptorFile); await writeDescriptor(modPath); } return(true); } async Task <bool> writeDescriptor(string fullPath) { bool?state = null; if (File.Exists(fullPath)) { var fileInfo = new System.IO.FileInfo(fullPath); state = fileInfo.IsReadOnly; fileInfo.IsReadOnly = false; } using var fs = new FileStream(fullPath, FileMode.Create, FileAccess.Write, FileShare.Read); var result = await WriteDescriptorToStreamAsync(parameters, fs); if (state.HasValue) { var fileInfo = new System.IO.FileInfo(fullPath); fileInfo.IsReadOnly = state.GetValueOrDefault(); } return(result); } var retry = new RetryStrategy(); return(await retry.RetryActionAsync(writeDescriptors)); }
/// <summary> /// write descriptor to stream as an asynchronous operation. /// </summary> /// <param name="parameters">The parameters.</param> /// <param name="stream">The stream.</param> /// <returns>Task<System.Boolean>.</returns> public async Task <bool> WriteDescriptorToStreamAsync(ModWriterParameters parameters, Stream stream) { using var sw = new StreamWriter(stream, leaveOpen: true); var props = parameters.Mod.GetType().GetProperties().Where(p => Attribute.IsDefined(p, typeof(DescriptorPropertyAttribute))); foreach (var prop in props) { var attr = Attribute.GetCustomAttribute(prop, typeof(DescriptorPropertyAttribute), true) as DescriptorPropertyAttribute; var val = prop.GetValue(parameters.Mod, null); if (val is IEnumerable <string> col) { if (col.Any()) { if (attr.KeyedArray) { foreach (var item in col) { await sw.WriteLineAsync($"{attr.PropertyName}=\"{item}\""); } } else { await sw.WriteLineAsync($"{attr.PropertyName}={{"); foreach (var item in col) { await sw.WriteLineAsync($"\t\"{item}\""); } await sw.WriteLineAsync("}"); } } } else { if (!string.IsNullOrWhiteSpace(val != null ? val.ToString() : string.Empty)) { if (attr.AlternateNameEndsWithCondition?.Count() > 0 && attr.AlternateNameEndsWithCondition.Any(p => val.ToString().EndsWith(p, StringComparison.OrdinalIgnoreCase))) { await sw.WriteLineAsync($"{attr.AlternatePropertyName}=\"{val}\""); } else { await sw.WriteLineAsync($"{attr.PropertyName}=\"{val}\""); } } } } await sw.FlushAsync(); return(true); }
/// <summary> /// Sets the descriptor lock asynchronous. /// </summary> /// <param name="parameters">The parameters.</param> /// <param name="isLocked">if set to <c>true</c> [is locked].</param> /// <returns>Task<System.Boolean>.</returns> public Task <bool> SetDescriptorLockAsync(ModWriterParameters parameters, bool isLocked) { var fullPath = Path.Combine(parameters.RootDirectory, parameters.Mod.DescriptorFile); if (File.Exists(fullPath)) { _ = new System.IO.FileInfo(fullPath) { IsReadOnly = isLocked }; return(Task.FromResult(true)); } return(Task.FromResult(false)); }
/// <summary> /// Deletes the descriptor asynchronous. /// </summary> /// <param name="parameters">The parameters.</param> /// <returns>Task<System.Boolean>.</returns> public Task <bool> DeleteDescriptorAsync(ModWriterParameters parameters) { var fullPath = Path.Combine(parameters.RootDirectory, parameters.Mod.DescriptorFile); if (File.Exists(fullPath)) { _ = new System.IO.FileInfo(fullPath) { IsReadOnly = false }; File.Delete(fullPath); return(Task.FromResult(true)); } return(Task.FromResult(false)); }
/// <summary> /// apply mods as an asynchronous operation. /// </summary> /// <param name="parameters">The parameters.</param> /// <returns>Task<System.Boolean>.</returns> public async Task <bool> ApplyModsAsync(ModWriterParameters parameters) { Task <bool>[] tasks; using (var mutex = await writeLock.LockAsync()) { tasks = new Task <bool>[] { Task.Run(async() => await sqliteExporter.ExportAsync(parameters)), Task.Run(async() => await jsonExporter.ExportAsync(parameters)) }; await Task.WhenAll(tasks); } return(tasks.All(p => p.Result)); }
/// <summary> /// Gets the patch mod directory. /// </summary> /// <param name="game">The game.</param> /// <param name="patchOrMergeName">Name of the patch or merge.</param> /// <returns>System.String.</returns> protected virtual string GetPatchModDirectory(IGame game, string patchOrMergeName) { var path = Path.Combine(game.UserDirectory, Shared.Constants.ModDirectory, patchOrMergeName); path = path.StandardizeDirectorySeparator(); var parameters = new ModWriterParameters() { Path = path }; if (!ModWriter.ModDirectoryExists(parameters) && !string.IsNullOrWhiteSpace(game.CustomModDirectory)) { path = Path.Combine(game.CustomModDirectory, patchOrMergeName).StandardizeDirectorySeparator(); } return(path); }
/// <summary> /// Exports the mods asynchronous. /// </summary> /// <param name="enabledMods">The mods.</param> /// <param name="regularMods">The regular mods.</param> /// <param name="collectionName">Name of the collection.</param> /// <returns>Task<System.Boolean>.</returns> public virtual async Task <bool> ExportModsAsync(IReadOnlyCollection <IMod> enabledMods, IReadOnlyCollection <IMod> regularMods, string collectionName) { var game = GameService.GetSelected(); if (game == null || enabledMods == null || regularMods == null) { return(false); } var allMods = GetInstalledModsInternal(game, false); var mod = GeneratePatchModDescriptor(allMods, game, GenerateCollectionPatchName(collectionName)); var applyModParams = new ModWriterParameters() { OtherMods = regularMods.Where(p => !enabledMods.Any(m => m.DescriptorFile.Equals(p.DescriptorFile))).ToList(), EnabledMods = enabledMods, RootDirectory = game.UserDirectory }; if (await ModWriter.ModDirectoryExistsAsync(new ModWriterParameters() { RootDirectory = game.UserDirectory, Path = mod.FileName })) { if (await ModWriter.WriteDescriptorAsync(new ModWriterParameters() { Mod = mod, RootDirectory = game.UserDirectory, Path = mod.DescriptorFile }, IsPatchModInternal(mod))) { applyModParams.TopPriorityMods = new List <IMod>() { mod }; Cache.Invalidate(ModsCachePrefix, ConstructModsCacheKey(game, true), ConstructModsCacheKey(game, false)); } } return(await ModWriter.ApplyModsAsync(applyModParams)); }
/// <summary> /// export as an asynchronous operation. /// </summary> /// <param name="parameters">The parameters.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> public async Task <bool> ExportAsync(ModWriterParameters parameters) { var dlcPath = Path.Combine(parameters.RootDirectory, Constants.DLC_load_path); var gameDataPath = Path.Combine(parameters.RootDirectory, Constants.Game_data_path); var modRegistryPath = Path.Combine(parameters.RootDirectory, Constants.Mod_registry_path); var dLCLoad = await LoadPdxModelAsync <DLCLoad>(dlcPath) ?? new DLCLoad(); var gameData = await LoadPdxModelAsync <GameData>(gameDataPath) ?? new GameData(); var modRegistry = await LoadPdxModelAsync <ModRegistryCollection>(modRegistryPath) ?? new ModRegistryCollection(); if (!parameters.AppendOnly) { gameData.ModsOrder.Clear(); dLCLoad.EnabledMods.Clear(); } // Remove invalid mods var toRemove = new List <string>(); foreach (var pdxMod in modRegistry) { if (pdxMod.Value.Status != Constants.Ready_to_play) { toRemove.Add(pdxMod.Key); } } foreach (var item in toRemove) { modRegistry.Remove(item); } if (parameters.EnabledMods != null) { foreach (var mod in parameters.EnabledMods) { SyncData(dLCLoad, gameData, modRegistry, mod, true); } } if (parameters.OtherMods != null) { foreach (var mod in parameters.OtherMods) { SyncData(dLCLoad, gameData, modRegistry, mod, false); } } if (parameters.TopPriorityMods != null) { foreach (var mod in parameters.TopPriorityMods) { var existingEntry = modRegistry.Values.FirstOrDefault(p => p.GameRegistryId.Equals(mod.DescriptorFile, StringComparison.OrdinalIgnoreCase)); if (existingEntry != null) { gameData.ModsOrder.Remove(existingEntry.Id); } var existingEnabledMod = dLCLoad.EnabledMods.FirstOrDefault(p => p.Equals(mod.DescriptorFile, StringComparison.OrdinalIgnoreCase)); if (!string.IsNullOrWhiteSpace(existingEnabledMod)) { dLCLoad.EnabledMods.Remove(existingEnabledMod); } SyncData(dLCLoad, gameData, modRegistry, mod, true); } } var tasks = new Task <bool>[] { WritePdxModelAsync(dLCLoad, dlcPath), WritePdxModelAsync(gameData, gameDataPath), WritePdxModelAsync(modRegistry, modRegistryPath), }; await Task.WhenAll(tasks); return(tasks.All(p => p.Result)); }
/// <summary> /// write descriptor as an asynchronous operation. /// </summary> /// <param name="parameters">The parameters.</param> /// <param name="writeDescriptorInModDirectory">if set to <c>true</c> [write descriptor in mod directory].</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> public async Task <bool> WriteDescriptorAsync(ModWriterParameters parameters, bool writeDescriptorInModDirectory) { async Task <bool> writeDescriptors() { // If needed I've got a much more complex serializer, it is written for Kerbal Space Program but the structure seems to be the same though this is much more simpler var fullPath = Path.Combine(parameters.RootDirectory ?? string.Empty, parameters.Path ?? string.Empty); await writeDescriptor(fullPath); if (writeDescriptorInModDirectory) { var modPath = Path.Combine(parameters.Mod.FileName, Shared.Constants.DescriptorFile); await writeDescriptor(modPath); } return(true); } async Task <bool> writeDescriptor(string fullPath) { using var fs = new FileStream(fullPath, FileMode.Create, FileAccess.Write, FileShare.Read); using var sw = new StreamWriter(fs); var props = parameters.Mod.GetType().GetProperties().Where(p => Attribute.IsDefined(p, typeof(DescriptorPropertyAttribute))); foreach (var prop in props) { var attr = Attribute.GetCustomAttribute(prop, typeof(DescriptorPropertyAttribute), true) as DescriptorPropertyAttribute; var val = prop.GetValue(parameters.Mod, null); if (val is IEnumerable <string> col) { if (col.Count() > 0) { await sw.WriteLineAsync($"{attr.PropertyName}={{"); foreach (var item in col) { await sw.WriteLineAsync($"\t\"{item}\""); } await sw.WriteLineAsync("}"); } } else { if (!string.IsNullOrWhiteSpace(val != null ? val.ToString() : string.Empty)) { if (attr.AlternateNameEndsWithCondition?.Count() > 0 && attr.AlternateNameEndsWithCondition.Any(p => val.ToString().EndsWith(p, StringComparison.OrdinalIgnoreCase))) { await sw.WriteLineAsync($"{attr.AlternatePropertyName}=\"{val}\""); } else { await sw.WriteLineAsync($"{attr.PropertyName}=\"{val}\""); } } } } await sw.FlushAsync(); return(true); } var retry = new RetryStrategy(); return(await retry.RetryActionAsync(writeDescriptors)); }
/// <summary> /// Gets the connection. /// </summary> /// <param name="parameters">The parameters.</param> /// <returns>IDbConnection.</returns> private IDbConnection GetConnection(ModWriterParameters parameters) { return(new SqliteConnection($"Data Source=\"{GetDbPath(parameters)}\"").EnsureOpen()); }
/// <summary> /// Gets the database path. /// </summary> /// <param name="parameters">The parameters.</param> /// <returns>System.String.</returns> private string GetDbPath(ModWriterParameters parameters) { return(Path.Combine(parameters.RootDirectory, Constants.Sql_db_path)); }
/// <summary> /// recreate collection as an asynchronous operation. /// </summary> /// <param name="parameters">The parameters.</param> /// <returns>Playsets.</returns> private async Task <Playsets> RecreateCollectionAsync(ModWriterParameters parameters) { Playsets getDefaultIronyCollection() { return(new Playsets() { Id = Guid.NewGuid().ToString(), IsActive = true, LoadOrder = CollectionSortType, Name = GetCollectionName() }); } var colName = GetCollectionName(); // They did do a cascade delete right? using var con = GetConnection(parameters); using var transaction = con.BeginTransaction(); var activeCollections = (await con.QueryAsync <Playsets>(p => p.IsActive == true, trace: trace)).ToList().Where(p => !p.Name.Equals(colName)).ToList(); try { Playsets ironyCollection; if (!parameters.AppendOnly) { await con.DeleteAsync <Playsets>(p => p.Name == colName, transaction : transaction, trace : trace); if (activeCollections.Count() > 0) { foreach (var item in activeCollections) { item.IsActive = false; } await con.UpdateAllAsync(activeCollections, transaction : transaction, trace : trace); } ironyCollection = getDefaultIronyCollection(); await con.InsertAsync(ironyCollection, transaction : transaction, trace : trace); } else { ironyCollection = (await con.QueryAsync <Playsets>(p => p.Name == colName, trace: trace)).FirstOrDefault(); if (activeCollections.Count() > 0) { foreach (var item in activeCollections) { item.IsActive = false; } await con.UpdateAllAsync(activeCollections, transaction : transaction, trace : trace); } if (ironyCollection == null) { ironyCollection = getDefaultIronyCollection(); await con.InsertAsync(ironyCollection, transaction : transaction, trace : trace); } else { ironyCollection.IsActive = true; await con.UpdateAsync(ironyCollection, transaction : transaction, trace : trace); } } transaction.Commit(); return(ironyCollection); } catch (Exception ex) { logger.Error(ex); transaction.Rollback(); throw; } }