/// <summary> /// Processes scaling jobs to scale image assets /// </summary> /// <param name="scalingJobs">The jobs to be processed</param> public static void BatchIMScale(List<ScalingJob> scalingJobs) { var options = new Options(); if (Parser.Default.ParseArgumentsStrict(Globals.Paths.Arguments, options) && options.Scale) { foreach (var scalingJob in scalingJobs) { string[] filePathInfo = (scalingJob.Path).Split('\\'); string game = filePathInfo[0]; string category = filePathInfo[1]; if (filePathInfo.Length > 3) { category += @"\" + filePathInfo[2]; } // Get the images to scale string[] inputImages; string startPath = Path.Combine(Globals.Paths.Assets, scalingJob.Path, "Source"); if (string.IsNullOrEmpty(scalingJob.ExcludePattern)) { inputImages = Directory.GetFiles(startPath, scalingJob.SearchPattern, SearchOption.AllDirectories); } else { string excludePattern = scalingJob.ExcludePattern; Regex r = new Regex($"^{excludePattern.Replace("*", ".*")}$", RegexOptions.IgnoreCase); inputImages = (Directory.GetFiles(startPath, scalingJob.SearchPattern, SearchOption.AllDirectories).Where(f => !r.IsMatch(Path.GetFileName(f)))).ToArray(); } //Get the desired image sizes if (File.Exists(Globals.Paths.ConfigurationFile)) { INIFile ini = new INIFile(Globals.Paths.ConfigurationFile); string[] outputDimensions = (ini.INIReadValue(game, category).Split(',')); foreach (string outputDimension in outputDimensions) { //Create a destination directory string outputWidth = outputDimension.Split('x')[0]; Directory.CreateDirectory(Path.Combine(Globals.Paths.Assets, scalingJob.Path, outputWidth)); foreach (string inputImage in inputImages) { // Get the extension so we only have to use IM identify when necessary string imageExtension = Path.GetExtension(inputImage); string inputDimensions = null; if (imageExtension == ".dds" || imageExtension == ".tga") { var imageMagickIdentify = new Process { StartInfo = new ProcessStartInfo { FileName = "identify.exe", Arguments = $" -format %wx%h {inputImage}", WindowStyle = ProcessWindowStyle.Hidden, UseShellExecute = false, RedirectStandardOutput = true, CreateNoWindow = true } }; imageMagickIdentify.Start(); inputDimensions = imageMagickIdentify.StandardOutput.ReadLine(); } else { Bitmap bitmap = new Bitmap(inputImage); inputDimensions = $"{bitmap.Width}x{bitmap.Height}"; bitmap.Dispose(); } string outputName = $"{Path.GetFileNameWithoutExtension(inputImage)}.png"; string destinationPath = Path.Combine(Globals.Paths.Assets, scalingJob.Path, outputWidth, outputName); // Only convert when the sizes are different otherwise just copy if (inputDimensions != outputDimension) { string imageMagickSettings = null; // Take care of the numerous cases switch (scalingJob.Path) { // Dota 2 Items case @"Dota 2\Items\": imageMagickSettings += "-gravity west -crop "; switch (inputDimensions) { case "128x64": imageMagickSettings += "87x64+0+0"; break; case "124x62": imageMagickSettings += "86x62+0+0"; break; case "128x128": imageMagickSettings += "128x128+0+0"; break; case "124x64": imageMagickSettings += "88x64+0+0"; break; } imageMagickSettings += " +repage"; break; // Smite Abilities case @"Smite\Abilities\": imageMagickSettings += "-alpha off"; switch (inputDimensions) { // Ability Banners case "256x128": imageMagickSettings += "-gravity center -crop 128x128+0+0 +repage"; break; default: break; } break; // Smite Gods case @"Smite\Gods\Portrait\": imageMagickSettings += "-alpha off -gravity west -crop 388x512+0+0 +repage"; break; default: // StarCraft II Upgrades and Abilities if ((scalingJob.Path == @"StarCraft II\Upgrades\") || (scalingJob.Path == @"StarCraft II\Abilities\")) { imageMagickSettings += "-shave 7x7 +repage"; } // Heroes of Newerth if (scalingJob.Path.StartsWith("Heroes of Newerth")) { imageMagickSettings += "-flip"; } break; } // Console.WriteLine("Scaling {0} from {1} to {2}", Path.GetFileName(inputImage), inputDimensions, outputDimension); var imageMagickMagick = new Process { StartInfo = new ProcessStartInfo { FileName = "magick.exe", Arguments = $" \"{inputImage}\" -colorspace RGB -size \"{inputDimensions}\" +sigmoidal-contrast 11.6933 -define filter:filter=Sinc -define filter:window=Jinc -define filter:lobes=3 {imageMagickSettings} -resize \"{outputDimension}\"! -sigmoidal-contrast 11.6933 -colorspace sRGB \"{destinationPath}\"", WindowStyle = ProcessWindowStyle.Hidden, UseShellExecute = false, RedirectStandardOutput = false, CreateNoWindow = true } }; imageMagickMagick.Start(); } else { File.Copy(inputImage, destinationPath, true); Console.WriteLine("Copying {0} to {1}", Path.GetFileName(inputImage), outputDimension); } } } } } } }
/// <summary> /// Renames files according to rename pairs found in CSV files in the 'data' directory /// </summary> /// <param name="game">The name of the game to process</param> public static void BatchFileRename(string game) { string[] csvFiles = Directory.GetFiles(Path.Combine(Globals.Paths.Data, game), "*.csv", SearchOption.AllDirectories).ToArray(); foreach (string csvFile in csvFiles) { List<string> renamePairsNotUsed = new List<string>(); string csvDirectory = Path.GetFileNameWithoutExtension(csvFile); List<string> assets = new List<string>(Directory.GetFiles(Path.Combine(Globals.Paths.Assets, game, csvDirectory), "*", SearchOption.AllDirectories)); using(var renamePairs = new StreamReader(csvFile)) { string line; while ((line = renamePairs.ReadLine()) != null) { string[] pair = line.Split(','); string oldName= pair[0]; string newName = pair[1]; string tempOldName = oldName; Regex r = new Regex($"^{tempOldName.Replace("*", ".*")}$", RegexOptions.IgnoreCase); string[] matches = assets.Where(f => r.IsMatch(Path.GetFileNameWithoutExtension(f))).ToArray(); if (matches.Length == 0) { renamePairsNotUsed.Add(oldName); continue; } foreach (string asset in matches) { string fileName = Path.GetFileName(asset); string fileExtension = Path.GetExtension(asset); string fileDirectoryName = Path.GetDirectoryName(asset); string destination = Path.Combine(fileDirectoryName, (newName + fileExtension)); if (!string.Equals(asset, destination, StringComparison.OrdinalIgnoreCase)) { Console.WriteLine("Renaming {0} to {1}", fileName, (newName + fileExtension)); File.Move(asset, destination); File.Delete(asset); } assets.Remove(asset); } } } // Don't log anything if logging is disabled. var options = new Options(); if (Parser.Default.ParseArgumentsStrict(Globals.Paths.Arguments, options) && options.Logging) { // Any leftover assets are those that are not in the csvs. if (assets.Count > 0) { assets.Sort(); const string notMatchedLogMessage = "-----This log lists any files that were not renamed. These names should be added to the csv file-----\r\n"; string notMatchedLogPath = (Globals.Paths.Logs + @"\log_unmatched_" + (game.Replace(" ", "-")) + "_" + csvDirectory + ".txt").ToLower(); string notMatchedLogContent = notMatchedLogMessage + string.Join("\r\n", assets.Select(f => Path.GetFileNameWithoutExtension(f)).ToArray()); File.AppendAllText(notMatchedLogPath, notMatchedLogContent, Encoding.UTF8); } // Any old names in the csv no longer being used should be removed if (renamePairsNotUsed.Count > 0) { renamePairsNotUsed.Sort(); const string notUsedLogMessage = "-----This log lists all old names that were not matched against any file names. For performance these old names should be removed from the csv file-----\r\n"; string notUsedLogPath = (Globals.Paths.Logs + @"\log_unused_" + (game.Replace(" ", "-")) + "_" + csvDirectory + ".txt").ToLower(); string notUsedLogContent = notUsedLogMessage + string.Join("\r\n", renamePairsNotUsed.ToArray()); File.AppendAllText(notUsedLogPath, notUsedLogContent, Encoding.UTF8); } } } }