Beispiel #1
0
        public static SpectrumCollection Read(byte[] bytes)
        {
            var header = _ParseHeader(bytes);
            var offset = HEADER_LENGTH;

            var fileType = header.FileType;
            IEnumerable <double> xValues = null;

            if (fileType.HasFlag(FileType.XYFile) && !fileType.HasFlag(FileType.OwnXArray))
            {
                xValues = Helper.ParseXValues(bytes, header.NumberOfPointsInFile, offset);
                offset += header.NumberOfPointsInFile * 4;
            }

            var subFiles = new List <SubFile>();

            for (var i = 0; i < header.NumberOfSubfiles; i++)
            {
                offset += _ParseSubFile(header, bytes, offset, out var subFile);
                if (fileType.HasFlag(FileType.XYFile) && !fileType.HasFlag(FileType.OwnXArray))
                {
                    subFile.XValues = xValues.ToArray();
                }
                subFiles.Add(subFile);
            }

            var logBlock = LogBlock.Empty;

            if (header.ByteOffsetToLogBlock != 0)
            {
                if (offset != header.ByteOffsetToLogBlock)
                {
                    throw new InvalidSpcFileException($"Invalid Log-File Offset");
                }
                logBlock = LogBlockParser.Parse(bytes, offset);
            }

            var spcFile = new SpcFile
            {
                Header   = header,
                SubFiles = subFiles,
                LogBlock = logBlock
            };

            return(new SpectrumCollection
            {
                Memo = spcFile.Header.Memo,
                BinaryData = spcFile.LogBlock.BinaryData,
                Date = spcFile.Header.Timestamp,
                ResolutionDescription = spcFile.Header.ResolutionDescriptionText,
                SourceInstrument = spcFile.Header.SourceInstrumentDescriptionText,
                XUnit = spcFile.Header.XUnit,
                YUnit = spcFile.Header.YUnit,
                MetaData = spcFile.LogBlock.TextData.KeyValues,
                Spectra = spcFile.SubFiles.Select(file => new Spectrum(file.XValues, file.YValues)).ToArray()
            });
        }
Beispiel #2
0
        private void LoadSpc(string path)
        {
            currentSPC         = new SpcFile();
            currentSPCFilename = path;
            currentSPC.Load(currentSPCFilename);
            statusText.Text = $"{currentSPCFilename} loaded.";

            SubFileListView.ItemsSource = currentSPC.Subfiles;
            InitFileSystemWatcher();
        }
        /// <summary>
        /// Clears the contents of <see cref="ArchiveListView"/> and repopulates it with the directories and files in the provided directory.
        /// </summary>
        /// <param name="file">The archive file whose contents will populate the view.</param>
        public void PopulateArchiveListView(string file)
        {
            CurrentArchiveFileInfo = new FileInfo(file);
            SpcFile tempSpc = new SpcFile();

            tempSpc.Load(CurrentArchiveFileInfo.FullName);

            ArchiveListView.Items.Clear();

            foreach (var subfile in tempSpc.Subfiles)
            {
                TextBlock tb = new TextBlock();
                tb.Text = subfile.Name;
                tb.MouseRightButtonDown += GenerateSubfileContextMenu;
                ArchiveListView.Items.Add(tb);
            }
        }
        /// <summary>
        /// Processes a previously-extracted subfile that's open in an editor. This process runs its translation steps backwards,
        /// before finally re-packing the final product into the original SPC file it came from.
        /// </summary>
        /// <param name="tempDir">The temporary directory where the extracted subfile data is located.</param>
        /// <param name="info">The <see cref="EditorTrackingInfo"/> associated with the editor and subfile.</param>
        private void RepackArchiveSubfile(string tempDir, EditorTrackingInfo info)
        {
            // First, run all translation steps in reverse
            var translationSteps    = info.SelectedAssociation.TranslationSteps;
            var translatedFilenames = info.SubfileNameHistory;

            for (int step = (translationSteps.Count - 1); step >= 0; --step)
            {
                // Resolve internal/external translator
                object resolvedTranslator = Config.FileAssociationConfig.ResolveInternalExternal(translationSteps[step]) 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(tempDir, translatedFilenames[step + 1]);

                    translatorProcess.Start();
                    translatorProcess.WaitForExit();
                }
                else
                {
                    // If we get here, there's been an error and we should abort
                    return;
                }
            }

            // The final translated filename is the original file & format, repack it
            SpcFile spc = new SpcFile();

            spc.Load(info.OriginArchivePath);
            string fullSubfilePath = Path.Combine(tempDir, translatedFilenames[0]);

            spc.InsertSubfile(fullSubfilePath);
            spc.Save(info.OriginArchivePath);
        }
Beispiel #5
0
        static void Main(string[] args)
        {
            Console.WriteLine("SPC Tool by CaptainSwag101\n" +
                              "Version 1.1.0, built on 2020-10-15\n");

            // Setup text encoding so we can use Shift-JIS text later on
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

            // Ensure we actually have some arguments, if not, print usage string
            if (args.Length == 0)
            {
                Console.WriteLine("Usage: SpcTool.exe <SPC file> (optional auto-execute commands and parameters, encapsulated in {})");
                Console.WriteLine("Example: SpcTool.exe test.spc {extract} {file1.srd *.txt} {insert} {file4.dat} {exit}");
                return;
            }

            // Parse input arguments
            // If the first argument is a valid SPC file (and if we reach this point it probably is), load it.
            string   loadedSpcName = args[0];
            FileInfo loadedSpcInfo = new FileInfo(loadedSpcName);

            if (!loadedSpcInfo.Exists)
            {
                Console.WriteLine($"ERROR: \"{loadedSpcName}\" does not exist.");
                return;
            }

            if (loadedSpcInfo.Extension.ToLowerInvariant() != ".spc")
            {
                Console.WriteLine("WARNING: Input file does not have the \".spc\" extension.\nIf you experience any issues, it means this file probably isn't an SPC archive.");
            }

            SpcFile loadedSpc = new SpcFile();

            loadedSpc.Load(loadedSpcName);

            // Combine any remaining args into a single long string to be broken down by our regex
            Queue <string>?autoExecQueue = null;

            if (args.Length > 1)
            {
                autoExecQueue = new Queue <string>();

                StringBuilder remainingArgsBuilder = new StringBuilder();
                remainingArgsBuilder.AppendJoin(" ", args[1..args.Length]);
        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 #7
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();
            }
        }