private void InitFileSystemWatcher() { if (fsWatch != null) { fsWatch.Dispose(); } tmpFilesDir = Path.Combine(TEMP_DIR, Path.GetFileNameWithoutExtension(currentSPCFilename)); Directory.CreateDirectory(tmpFilesDir); // We want to see when our temporary files have changed // So we can automatically update them in the SPC. fsWatch = new FileSystemWatcher(tmpFilesDir); fsWatch.Changed += async(sender, e) => { if (currentSPC.Subfiles.Select(x => x.Name).Contains(e.Name)) { // Wait for file to become unlocked... while (true) { try { fsWatch.EnableRaisingEvents = false; await currentSPC.InsertSubfileAsync(e.FullPath); if (!Directory.Exists(tmpFilesDir)) { return; } fsWatch.EnableRaisingEvents = true; } catch (IOException) { await Task.Delay(50); } } } }; }
static void Main(string[] args) { Console.WriteLine("SPC Tool by CaptainSwag101\n" + "Version 0.0.2, built on 2019-09-25\n"); // Parse input argument if (args.Length == 0) { return; } FileInfo info = new FileInfo(args[0]); if (!info.Exists) { Console.WriteLine($"ERROR: \"{args[0]}\" does not exist."); return; } if (info.Extension.ToLowerInvariant() != ".spc") { Console.WriteLine("ERROR: Input file does not have the \".spc\" extension."); return; } // If the first argument is a valid SPC file (and if we reach this point it probably is), it is the input. input = args[0]; // Parse operation argument // If command starts with "--", it is our operation to perform if (args.Length > 1 && args[1].StartsWith("--")) { string op = args[1].TrimStart('-').ToLowerInvariant(); if (validOperations.Keys.Any(op.Contains)) { operation = op; } else { Console.WriteLine("ERROR: Invalid operation specified."); return; } } // Parse target arguments for (int i = 2; i < args.Length; ++i) { targets.Add(args[i]); } // Load the input file SpcFile spc = new SpcFile(); spc.Load(input); // Execute operation if (operation == null) { Console.WriteLine("ERROR: No operation specified."); } else if (validOperations[operation] != -1 && targets.Count != validOperations[operation]) { Console.WriteLine($"ERROR: Invalid number of target(s) specified, expected {validOperations[operation]}."); } else { switch (operation) { case "list": case "bench": Console.WriteLine($"\"{info.Name}\" contains the following subfiles:\n"); foreach (SpcSubfile subfile in spc.Subfiles) { Console.WriteLine($"Subfile name: \"{subfile.Name}\""); Console.WriteLine($"\tCompression flag: {subfile.CompressionFlag}"); Console.WriteLine($"\tUnknown flag: {subfile.UnknownFlag}"); Console.WriteLine($"\tCurrent size: {subfile.CurrentSize.ToString("n0")} bytes"); Console.WriteLine($"\tOriginal size: {subfile.OriginalSize.ToString("n0")} bytes"); // Benchmark decompression and compression if (operation == "bench") { Stopwatch stopwatch = new Stopwatch(); Console.Write("Decompressing..."); stopwatch.Start(); subfile.Decompress(); stopwatch.Stop(); Console.WriteLine($" Done! Took {stopwatch.Elapsed.ToString()}"); Console.Write("Compressing..."); stopwatch.Restart(); subfile.Compress(); stopwatch.Stop(); Console.WriteLine($" Done! Took {stopwatch.Elapsed.ToString()}"); } Console.WriteLine(); } break; case "extract": // Setup an output directory for extracted files output ??= info.DirectoryName + Path.DirectorySeparatorChar + info.Name.Substring(0, info.Name.Length - info.Extension.Length); Directory.CreateDirectory(output); // Generate list of subfiles to be extracted that match the target regex List <string> subfilesToExtract = new List <string>(); foreach (string target in targets) { string regexTarget = "^" + Regex.Escape(target).Replace("\\?", ".").Replace("\\*", ".*") + "$"; foreach (SpcSubfile subfile in spc.Subfiles) { if (Regex.IsMatch(subfile.Name, regexTarget)) { subfilesToExtract.Add(subfile.Name); } } } // Extract the subfiles using Tasks Task[] extractTasks = new Task[subfilesToExtract.Count]; // IMPORTANT: If we ever switch to a for loop instead of foreach, // make sure to make a local scoped copy of the subfile name in order to prevent // threading weirdness from passing the wrong string value and causing random issues. foreach (string subfileName in subfilesToExtract) { Console.WriteLine($"Extracting \"{subfileName}\"..."); extractTasks[subfilesToExtract.IndexOf(subfileName)] = Task.Factory.StartNew(() => spc.ExtractSubfile(subfileName, output)); } // Wait until all target subfiles have been extracted Task.WaitAll(extractTasks); break; case "insert": // Insert the subfiles using Tasks Task[] insertTasks = new Task[targets.Count]; // IMPORTANT: If we ever switch to a for loop instead of foreach, // make sure to make a local scoped copy of the subfile name in order to prevent // threading weirdness from passing the wrong string value and causing random issues. foreach (string subfileName in targets) { Console.WriteLine($"Inserting \"{subfileName}\"..."); insertTasks[targets.IndexOf(subfileName)] = spc.InsertSubfileAsync(subfileName, confirmation: ConfirmOverwrite); } // Wait until all target subfiles have been inserted foreach (Task task in insertTasks) { task.Start(); task.Wait(); } // Save the spc file spc.Save(input); break; } } if (pauseAfterComplete) { Console.WriteLine("Press Enter to close..."); Console.Read(); } }