/// <summary> /// Processes work on two file /// </summary> /// <param name="main">The path to the main file.</param> /// <param name="secondary">The path to the secondary file.</param> /// <param name="target">The path to the target file.</param> /// <param name="messageHandler">The message handler.</param> /// <param name="init">The initialization label.</param> /// <param name="action">The action to process.</param> /// <returns></returns> private async Task WorkOnTwoFiles(string main, string secondary, string target, IMessageHandler messageHandler, string init, Func <CsvGamesList, CsvGamesList, CsvGamesList> action) { messageHandler.Init(init); try { var steps = 5; var current = 0; var fiMain = new FileInfo(main); var fiSecondary = new FileInfo(secondary); var fiTarget = new FileInfo(target); messageHandler.Progress($"reading file {fiMain.Name}", steps, ++current); var mainEntries = await ReadFile(main, true); messageHandler.Progress($"reading file {fiSecondary.Name}", steps, ++current); var secondaryEntries = await ReadFile(secondary, true); messageHandler.Progress($"action", steps, ++current); var result = action(mainEntries, secondaryEntries); messageHandler.Progress($"save to file {fiTarget.Name}", steps, ++current); await WriteFile(result, target); messageHandler.Done($"Done! Result has {result.Games.Count} entries", target); } catch (Exception ex) { messageHandler.Error(ex); } }
/// <summary> /// Converts a DAT file. /// </summary> /// <param name="main">The main file.</param> /// <param name="target">The target file.</param> /// <param name="messageHandler">The message handler.</param> public async Task ConvertDat(string main, string target, IMessageHandler messageHandler) { messageHandler.Init("DAT conversion"); try { // read input file using (var reader = File.OpenRead(main)) { // deserialize file var datFile = Serializer.DeserializeXml <Models.DatFile.Datafile>(reader); // output stream using (var outStream = new FileStream(target, FileMode.Create, FileAccess.Write)) { using (var outStreamWriter = new StreamWriter(outStream)) { await outStreamWriter.WriteLineAsync(headerDatRow); // list entries depending on if it's a list of machines or games IEnumerable <Models.DatFile.BaseEntry> entries = datFile.Game != null && datFile.Game.Any() ? datFile.Game : datFile.Machine; int total = entries.Count(); int i = 0; foreach (var e in entries) { i++; messageHandler.Progress($"Converting {e.Name}", total, i); var sb = new StringBuilder(); sb.Append($"{e.Name}{defaultDelimiter}"); sb.Append($"{e.Description ?? "-"}{defaultDelimiter}"); sb.Append($"{e.Year ?? "-"}{defaultDelimiter}"); sb.Append($"{e.Manufacturer ?? "-"}{defaultDelimiter}"); if (e is Models.DatFile.Game) { var eg = e as Models.DatFile.Game; sb.Append(string.IsNullOrEmpty(eg.Cloneof) ? "NO" : "YES").Append(defaultDelimiter); // is_parent sb.Append(eg.Romof ?? "-").Append(defaultDelimiter); // romof sb.Append(string.IsNullOrEmpty(eg.Cloneof) ? "YES" : "NO").Append(defaultDelimiter); // is_clone sb.Append(eg.Cloneof ?? "-").Append(defaultDelimiter); // cloneof sb.Append(eg.Sampleof ?? "-").Append(defaultDelimiter); // sampleof } else { sb.Append("-;-;-;-;-;"); } await outStreamWriter.WriteLineAsync(sb.ToString()); } } } messageHandler.Done("DAT file converted", target); } } catch (Exception ex) { messageHandler.Error(ex); } }
/// <summary> /// Deletes roms from a folder /// </summary> /// <param name="args">The arguments</param> /// <param name="messageHandler">The message handler.</param> /// <exception cref="FileNotFoundException">Unable to find main CSV file</exception> /// <exception cref="DirectoryNotFoundException">Unable to find selection folder {args.selection}</exception> public async Task Delete(Actions.RomsAction args, IMessageHandler messageHandler) { messageHandler.Init("Deleting roms"); try { // check files and folders if (!File.Exists(args.main)) { throw new FileNotFoundException("Unable to find main CSV file", args.main); } if (!Directory.Exists(args.selection)) { throw new DirectoryNotFoundException($"Unable to find selection folder {args.selection}"); } // read CSV file var content = await csvService.ReadFile(args.main, false); var total = content.Games.Count(); var i = 0; var deleted = 0; foreach (var f in content.Games) { if (messageHandler.MustCancel) { break; } i++; // build vars var game = f.Name; var zip = $"{game}.zip"; var filePath = Path.Join(args.selection, zip); messageHandler.Progress(game, total, i); if (File.Exists(filePath)) { File.Delete(filePath); deleted++; } } messageHandler.Done($"Deleted {deleted} file(s)", args.selection); } catch (Exception ex) { messageHandler.Error(ex); } }
/// <summary> /// Keeps only listed roms in a folder /// </summary> /// <param name="args">The arguments</param> /// <param name="messageHandler">The message handler.</param> /// <exception cref="FileNotFoundException">Unable to find main CSV file</exception> /// <exception cref="DirectoryNotFoundException">Unable to find selection folder {args.selection}</exception> public async Task Keep(Actions.RomsAction args, IMessageHandler messageHandler) { messageHandler.Init("Filtering roms"); try { // check files and folders if (!File.Exists(args.main)) { throw new FileNotFoundException("Unable to find main CSV file", args.main); } if (!Directory.Exists(args.selection)) { throw new DirectoryNotFoundException($"Unable to find selection folder {args.selection}"); } // read CSV file var content = await csvService.ReadFile(args.main, false); // get list of files var files = (new DirectoryInfo(args.selection)).GetFiles("*.zip"); var total = content.Games.Count(); var i = 0; var deleted = 0; // check if files exist in games list foreach (var f in files) { if (messageHandler.MustCancel) { break; } i++; messageHandler.Progress(f.Name, total, i); if (!content.Games.Any(c => $"{c.Name}.zip" == f.Name)) { File.Delete(f.FullName); deleted++; } } messageHandler.Done($"Deleted {deleted} files", args.selection); } catch (Exception ex) { messageHandler.Error(ex); } }
/// <summary> /// Lists the files in a folder to a CSV file. /// </summary> /// <param name="main">The main folder.</param> /// <param name="target">The target CSV file.</param> /// <param name="messageHandler">The message handler.</param> /// <exception cref="DirectoryNotFoundException">Unable to find the folder {main}</exception> public async Task ListFiles(string main, string target, IMessageHandler messageHandler) { messageHandler.Init("List files to a CSV"); try { if (!Directory.Exists(main)) { throw new DirectoryNotFoundException($"Unable to find the folder {main}"); } var di = new DirectoryInfo(main); using (var output = new StreamWriter(target, false)) { await output.WriteLineAsync($"{nameColumn}{defaultDelimiter}"); var files = di.GetFiles("*.zip", new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive }); var total = files.Length; var i = 0; foreach (var f in files) { i++; messageHandler.Progress($"Listing file {f.Name}", total, i); await output.WriteLineAsync(f.Name.Replace(".zip", "", StringComparison.InvariantCultureIgnoreCase) + defaultDelimiter); } } messageHandler.Done("Files listed", target); } catch (Exception ex) { messageHandler.Error(ex); } }
/// <summary> /// Copies roms /// </summary> /// <param name="args">The arguments</param> /// <param name="messageHandler">The message handler.</param> /// <exception cref="FileNotFoundException">Unable to find main CSV file</exception> /// <exception cref="DirectoryNotFoundException">Unable to find romset folder {args.romset}</exception> public async Task Add(Actions.RomsAction args, IMessageHandler messageHandler) { messageHandler.Init("Copying roms"); try { // check files and folders if (!File.Exists(args.main)) { throw new FileNotFoundException("Unable to find main CSV file", args.main); } if (!Directory.Exists(args.romset)) { throw new DirectoryNotFoundException($"Unable to find romset folder {args.romset}"); } if (!Directory.Exists(args.selection)) { Directory.CreateDirectory(args.selection); } // read CSV file var content = await csvService.ReadFile(args.main, false); var total = content.Games.Count(); var i = 0; var copied = 0; // copy each file found in CSV foreach (var f in content.Games) { if (messageHandler.MustCancel) { break; } i++; // build vars var game = f.Name; var zip = $"{game}.zip"; var sourceRom = Path.Join(args.romset, zip); var destRom = Path.Join(args.selection, zip); if (File.Exists(sourceRom)) { var fi = new FileInfo(sourceRom); messageHandler.Progress($"{game} ({FileSystem.HumanSize(fi.Length)})", total, i); // copy rom if (!File.Exists(destRom) || args.overwrite) { File.Copy(sourceRom, destRom, true); copied++; } // try to copy chd if it can be found var sourceChd = Path.Join(args.romset, game); var targetChd = Path.Join(args.selection, game); if (Directory.Exists(sourceChd)) { if (messageHandler.MustCancel) { break; } messageHandler.Progress($"Copying {game} CHD ({FileSystem.HumanSize(FileSystem.DirectorySize(sourceChd))})", total, i); FileSystem.DirectoryCopy(sourceChd, targetChd, args.overwrite, false); } } } messageHandler.Done($"Copied {copied} file(s)", args.selection); } catch (Exception ex) { messageHandler.Error(ex); } }
/// <summary> /// Downloads an overlay pack /// </summary> /// <param name="data">The parameters</param> /// <param name="messageHandler">The message handler.</param> /// <exception cref="FileNotFoundException">Unable to parse rom config {game} to find overlay (input_overlay) /// or /// Unable to parse overlay config {game} to find image (overlay0_overlay)</exception> public async Task Download(Actions.OverlaysAction data, IMessageHandler messageHandler) { messageHandler.Init("Download overlay pack"); try { var os = ArcadeManagerEnvironment.SettingsOs; var pack = ArcadeManagerEnvironment.AppData.Overlays.Where(o => o.Name == data.pack).First(); // check if the destination of rom cfg is the rom folder var romCfgFolder = pack.Roms.Dest[os] == "roms" ? null // save rom cfg directly into rom folder(s) : Path.Join(data.configFolder, pack.Roms.Dest[os]); // save rom cfg in config folder // list the available rom configs messageHandler.Progress("list of files to download", 1, 100); var romConfigs = await downloaderService.ListFiles(pack.Repository, pack.Roms.Src); // download common files messageHandler.Progress("common files", 1, 100); if (pack.Common != null && !string.IsNullOrWhiteSpace(pack.Common.Src)) { await DownloadCommon(pack, Path.Join(data.configFolder, pack.Common.Dest[os]), data.overwrite, data.ratio, messageHandler, 100, 1); } if (messageHandler.MustCancel) { throw new Exception("Operation cancelled"); } // check that thvere is a matching game in any of the roms folders messageHandler.Progress("games list to process", 1, 100); var processedOverlays = new List <string>(); var romsToProcess = GetRomsToProcess(data.romFolders, romConfigs.Tree); var total = romConfigs.Tree.Count; var current = 0; var installed = 0; foreach (var r in romsToProcess.OrderBy(r => r.Game)) { if (messageHandler.MustCancel) { throw new Exception("Operation cancelled"); } current++; var game = r.Game; messageHandler.Progress($"{game}: download overlay (rom config)", total, current); // download the rom config and extract the overlay file name var romConfigContent = string.Empty; foreach (var romFolder in r.TargetFolder) { if (messageHandler.MustCancel) { throw new Exception("Operation cancelled"); } var romConfigFile = Path.Join(romFolder, $"{game}.zip.cfg"); // get rom config content if (string.IsNullOrEmpty(romConfigContent)) { if (data.overwrite || !File.Exists(romConfigFile)) { // file doesn't exist or we'll overwrite it romConfigContent = await downloaderService.DownloadFileText(pack.Repository, $"{pack.Roms.Src}/{game}.zip.cfg"); // fix resolution and paths romConfigContent = ChangeResolution(romConfigContent, data.ratio); romConfigContent = FixPaths(romConfigContent, pack); } else { // file exist, we don"t overwrite: read it romConfigContent = await File.ReadAllTextAsync(romConfigFile); } } // write rom config if (data.overwrite || !File.Exists(romConfigFile)) { if (messageHandler.MustCancel) { throw new Exception("Operation cancelled"); } await File.WriteAllTextAsync(romConfigFile, romConfigContent); installed++; } } if (messageHandler.MustCancel) { throw new Exception("Operation cancelled"); } messageHandler.Progress($"{game}: download overlay (config)", total, current); // extract the overlay file name var overlayPath = GetCfgData(romConfigContent, "input_overlay"); if (string.IsNullOrWhiteSpace(overlayPath)) { throw new FileNotFoundException($"Unable to parse rom config {game} to find overlay (input_overlay)"); } var overlayFi = new FileInfo(overlayPath); var overlayConfigDest = Path.Join(data.configFolder, overlayFi.Name); // download the overlay file name and extract the image file name var overlayConfigContent = string.Empty; if (data.overwrite || !File.Exists(overlayConfigDest)) { if (messageHandler.MustCancel) { throw new Exception("Operation cancelled"); } overlayConfigContent = await downloaderService.DownloadFileText(pack.Repository, $"{pack.Overlays.Src}/{overlayFi.Name}"); // fix path overlayConfigContent = FixPaths(overlayConfigContent, pack); await File.WriteAllTextAsync(overlayConfigDest, overlayConfigContent); } else { overlayConfigContent = File.ReadAllText(overlayConfigDest); } if (messageHandler.MustCancel) { throw new Exception("Operation cancelled"); } messageHandler.Progress($"{game}: download overlay (image)", total, current); // extract the image file name var imagePath = GetCfgData(overlayConfigContent, "overlay0_overlay"); if (string.IsNullOrWhiteSpace(imagePath)) { throw new FileNotFoundException($"Unable to parse overlay config {game} to find image (overlay0_overlay)"); } var imageFi = new FileInfo(imagePath); var imageDest = Path.Join(data.configFolder, imageFi.Name); // download the image if (data.overwrite || !File.Exists(imageDest)) { if (messageHandler.MustCancel) { throw new Exception("Operation cancelled"); } await downloaderService.DownloadFile(pack.Repository, $"{pack.Overlays.Src}/{imageFi.Name}", imageDest); } } messageHandler.Done($"Installed {installed} overlays", null); } catch (Exception ex) { messageHandler.Error(ex); } }
/// <summary> /// Converts a INI file to CSV /// </summary> /// <param name="main">The main file</param> /// <param name="target">The target folder to create files into</param> /// <param name="messageHandler">The message handler.</param> public async Task ConvertIni(string main, string target, IMessageHandler messageHandler) { messageHandler.Init("INI conversion"); try { var data = new Dictionary <string, List <IniEntry> >(); var mainInfo = new FileInfo(main); using (var source = new StreamReader(main)) { var isFolderSetting = false; var currentSection = ""; while (!source.EndOfStream) { var line = (await source.ReadLineAsync()).Trim(); // progress up to 50% messageHandler.Progress("Reading source file", 100, (int)(source.BaseStream.Position / mainInfo.Length * 50)); // ignore empty lines and comments if (string.IsNullOrWhiteSpace(line) || line.StartsWith(";")) { continue; } // ignore folder settings if (line == "[FOLDER_SETTINGS]") { isFolderSetting = true; continue; } if (isFolderSetting && !line.StartsWith("[")) { continue; } // we're out of folder settings isFolderSetting = false; // found a section if (line.StartsWith("[")) { currentSection = line; // add to data if (!data.ContainsKey(currentSection)) { data.Add(currentSection, new List <IniEntry>()); } continue; } // we're in a section data if (line.IndexOf("=") > 0) { // game=value var split = line.Split("=", StringSplitOptions.TrimEntries); data[currentSection].Add(new IniEntry { game = split[0], value = split[1] }); } else { // simple games list data[currentSection].Add(new IniEntry { game = line }); } } } // create a file for each non-empty section var i = 0; foreach (var entry in data.Where(d => d.Value.Any())) { i++; // file name = sanitized section name, or source name if there's only one section var name = data.Count > 1 ? Sanitize(entry.Key.Trim("[]".ToCharArray())) : mainInfo.Name.Replace(".ini", ""); name += ".csv"; var path = Path.Join(target, name); // create increments to not overwrite existing conversions var j = 0; var finalPath = path; while (File.Exists(finalPath)) { finalPath = path.Replace(".csv", $" ({++j}).csv"); } path = finalPath; // progress from 50% messageHandler.Progress($"Creating file {name}", 100, 50 + (i / data.Count * 50)); // write into file using (var output = new StreamWriter(path)) { await output.WriteLineAsync(headerIniRow); foreach (var iniEntry in entry.Value) { await output.WriteLineAsync($"{iniEntry.game}{defaultDelimiter}{iniEntry.value}{defaultDelimiter}"); } } } messageHandler.Done("INI file converted", target); } catch (Exception ex) { messageHandler.Error(ex); } }