Beispiel #1
0
        private void Extract(object sender, RoutedEventArgs e)
        {
            var dialog = new CommonOpenFileDialog
            {
                Title          = "Select Extract Location...",
                IsFolderPicker = true
            };

            if (dialog.ShowDialog() != CommonFileDialogResult.Ok)
            {
                return;
            }

            statusText.Text = $"Extracting {SubFileListView.SelectedItems.Count} files...";
            foreach (var item in SubFileListView.SelectedItems)
            {
                if (item is SpcSubfile file)
                {
                    currentSPC.ExtractSubfile(file.Name, dialog.FileName);
                }
            }
            statusText.Text = $"Successfully extracted {SubFileListView.SelectedItems.Count} files to {dialog.FileName}.";
        }
        private void OpenSelectedArchiveSubfile(object sender, int associationIndex = 0)
        {
            if (sender == null || !(sender is ListView))
            {
                return;
            }

            if (!((sender as ListView).SelectedItem is TextBlock selectedTextBlock))
            {
                return;
            }


            // Open an editor based on target file type
            string targetFileExt = Path.GetExtension(selectedTextBlock.Text).ToLowerInvariant();

            // Throw an error if we can't find a valid editor
            if (!Config.FileAssociationConfig.AssociationList.ContainsKey(targetFileExt))
            {
                throw new NotImplementedException($"The filetype {targetFileExt} is not supported.");
            }

            // Create the inner temp directory
            //string trackingHash = (CurrentArchivePath + originTextBlock.Text).GetHashCode().ToString("X8");
            string trackingHash     = Path.GetRandomFileName();
            string generatedTempDir = Path.Combine(AppTempDirInfo.FullName, trackingHash);

            //string tempFileLocation = Path.Combine(generatedTempDir, originTextBlock.Text);
            Directory.CreateDirectory(generatedTempDir);

            // Extract the subfile to a temporary folder
            SpcFile temp = new SpcFile();

            temp.Load(CurrentArchiveFileInfo.FullName);
            temp.ExtractSubfile(selectedTextBlock.Text, generatedTempDir);


            // Default to the first association in the list
            var association = Config.FileAssociationConfig.AssociationList[targetFileExt][associationIndex];

            // Process translation steps, if any
            List <string> translatedOutputHistory = new List <string>();

            translatedOutputHistory.Add(selectedTextBlock.Text);
            foreach (string translationStep in association.TranslationSteps)
            {
                object resolvedTranslator = Config.FileAssociationConfig.ResolveInternalExternal(translationStep) as Process;

                // Run the translation step
                if (resolvedTranslator is Window)
                {
                    Window translatorWindow = resolvedTranslator as Window;

                    // Finally, open the target editor window as blocking
                    translatorWindow.ShowDialog();
                }
                else if (resolvedTranslator is Process)
                {
                    Process translatorProcess = resolvedTranslator as Process;

                    // Setup the target process' launch args
                    translatorProcess.StartInfo.Arguments = Path.Combine(generatedTempDir, translatedOutputHistory.Last());

                    translatorProcess.Start();
                    translatorProcess.WaitForExit();
                }
                else
                {
                    // If we get here, there's been an error and we should abort and clean up
                    Directory.Delete(generatedTempDir, true);
                }

                // Check for any extra files and use the first that isn't our starting file as the translated output
                List <string> newFiles = new List <string>();
                foreach (var file in Directory.EnumerateFiles(generatedTempDir))
                {
                    FileInfo tempInfo = new FileInfo(file);

                    if (!translatedOutputHistory.Contains(tempInfo.Name))
                    {
                        newFiles.Add(tempInfo.Name);
                    }
                }

                // TODO: If multiple new files are generated, prompt the user for which file to use as the output
                foreach (string newFile in newFiles)
                {
                    translatedOutputHistory.Add(newFile);
                    break;
                }
            }

            // Get editor window if association is internal, launch external program otherwise
            object resolvedEditor = Config.FileAssociationConfig.ResolveInternalExternal(association.EditorProgram);

            // Add the target editor to our tracking database (use the non-translated subfile name here!!!)
            ActiveFileDatabase.Add(generatedTempDir, new EditorTrackingInfo {
                Editor              = resolvedEditor,
                OriginArchivePath   = CurrentArchiveFileInfo.FullName,
                SubfileNameHistory  = translatedOutputHistory,
                SelectedAssociation = association
            });

            if (resolvedEditor is Window && resolvedEditor is IFileEditor)
            {
                Window editorWindow = resolvedEditor as Window;

                // Subscribe to the editor's closed event so we can rebuild its target archive and clean up any temporary files
                editorWindow.Closed += OnEditorWindowClosed;

                // Open the target file's data in the editor
                (resolvedEditor as IFileEditor).LoadFile(Path.Combine(generatedTempDir, translatedOutputHistory.Last()));

                // Finally, open the target editor window
                editorWindow.Show();
            }
            else if (resolvedEditor is Process)
            {
                Process editorProcess = resolvedEditor as Process;

                // Subscribe to the editor's exit event so we can rebuild its target archive and clean up any temporary files
                editorProcess.EnableRaisingEvents = true;
                editorProcess.Exited += OnEditorProcessExited;

                // Setup the target process' launch args
                editorProcess.StartInfo.Arguments = generatedTempDir + Path.DirectorySeparatorChar + translatedOutputHistory.Last();

                // Finally, open the target editor window
                editorProcess.Start();
            }
            else
            {
                // If we get here, there's been an error and we should abort and clean up
                Directory.Delete(generatedTempDir, true);
                ActiveFileDatabase.Remove(generatedTempDir);
            }
        }
Beispiel #3
0
        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();
            }
        }