예제 #1
0
        /// <summary>
        /// Seek to a specific point in the stream, if possible
        /// </summary>
        /// <param name="input">Input stream to try seeking on</param>
        /// <param name="offset">Optional offset to seek to</param>
        public static long SeekIfPossible(this Stream input, long offset = 0)
        {
            try
            {
                if (input.CanSeek)
                {
                    if (offset < 0)
                    {
                        return(input.Seek(offset, SeekOrigin.End));
                    }
                    else if (offset >= 0)
                    {
                        return(input.Seek(offset, SeekOrigin.Begin));
                    }
                }

                return(input.Position);
            }
            catch (NotSupportedException ex)
            {
                LoggerImpl.Verbose(ex, "Stream does not support seeking to starting offset. Stream position not changed");
            }
            catch (NotImplementedException ex)
            {
                LoggerImpl.Warning(ex, "Stream does not support seeking to starting offset. Stream position not changed");
            }

            return(-1);
        }
예제 #2
0
        /// <summary>
        /// Retrieve a list of just directories from inputs
        /// </summary>
        /// <param name="inputs">List of strings representing directories and files</param>
        /// <param name="appendparent">True if the parent name should be included in the ParentablePath, false otherwise (default)</param>
        /// <returns>List of strings representing just directories from the inputs</returns>
        public static List <ParentablePath> GetDirectoriesOnly(List <string> inputs, bool appendparent = false)
        {
            List <ParentablePath> outputs = new List <ParentablePath>();

            for (int i = 0; i < inputs.Count; i++)
            {
                string input = inputs[i];

                // If we have a null or empty path
                if (string.IsNullOrEmpty(input))
                {
                    continue;
                }

                // If we have a wildcard
                string pattern = "*";
                if (input.Contains("*") || input.Contains("?"))
                {
                    pattern = Path.GetFileName(input);
                    input   = input.Substring(0, input.Length - pattern.Length);
                }

                // Get the parent path in case of appending
                string parentPath;
                try
                {
                    parentPath = Path.GetFullPath(input);
                }
                catch (Exception ex)
                {
                    LoggerImpl.Error(ex, $"An exception occurred getting the full path for '{input}'");
                    continue;
                }

                if (Directory.Exists(input))
                {
                    List <string> directories = GetDirectoriesOrdered(input, pattern);
                    foreach (string dir in directories)
                    {
                        try
                        {
                            outputs.Add(new ParentablePath(Path.GetFullPath(dir), appendparent ? parentPath : string.Empty));
                        }
                        catch (PathTooLongException ex)
                        {
                            LoggerImpl.Warning(ex, $"The path for '{dir}' was too long");
                        }
                        catch (Exception ex)
                        {
                            LoggerImpl.Error(ex, $"An exception occurred processing '{dir}'");
                        }
                    }
                }
            }

            return(outputs);
        }
예제 #3
0
        /// <summary>
        /// Retrieve file information for a single file
        /// </summary>
        /// <param name="input">Filename to get information from</param>
        /// <param name="size">Size of the input stream</param>
        /// <param name="hashes">Hashes to include in the information</param>
        /// <param name="keepReadOpen">True if the underlying read stream should be kept open, false otherwise</param>
        /// <returns>Populated BaseFile object if success, empty one on error</returns>
        public static BaseFile GetInfo(Stream input, long size = -1, Hash hashes = Hash.Standard, bool keepReadOpen = false)
        {
            // If we want to automatically set the size
            if (size == -1)
            {
                size = input.Length;
            }

            try
            {
                // Get a list of hashers to run over the buffer
                List <Hasher> hashers = new List <Hasher>();

                if (hashes.HasFlag(Hash.CRC))
                {
                    hashers.Add(new Hasher(Hash.CRC));
                }
                if (hashes.HasFlag(Hash.MD5))
                {
                    hashers.Add(new Hasher(Hash.MD5));
                }
                if (hashes.HasFlag(Hash.SHA1))
                {
                    hashers.Add(new Hasher(Hash.SHA1));
                }
                if (hashes.HasFlag(Hash.SHA256))
                {
                    hashers.Add(new Hasher(Hash.SHA256));
                }
                if (hashes.HasFlag(Hash.SHA384))
                {
                    hashers.Add(new Hasher(Hash.SHA384));
                }
                if (hashes.HasFlag(Hash.SHA512))
                {
                    hashers.Add(new Hasher(Hash.SHA512));
                }
                if (hashes.HasFlag(Hash.SpamSum))
                {
                    hashers.Add(new Hasher(Hash.SpamSum));
                }

                // Initialize the hashing helpers
                var    loadBuffer = new ThreadLoadBuffer(input);
                int    buffersize = 3 * 1024 * 1024;
                byte[] buffer0    = new byte[buffersize];
                byte[] buffer1    = new byte[buffersize];

                /*
                 * Please note that some of the following code is adapted from
                 * RomVault. This is a modified version of how RomVault does
                 * threaded hashing. As such, some of the terminology and code
                 * is the same, though variable names and comments may have
                 * been tweaked to better fit this code base.
                 */

                // Pre load the first buffer
                long refsize = size;
                int  next    = refsize > buffersize ? buffersize : (int)refsize;
                input.Read(buffer0, 0, next);
                int current = next;
                refsize -= next;
                bool bufferSelect = true;

                while (current > 0)
                {
                    // Trigger the buffer load on the second buffer
                    next = refsize > buffersize ? buffersize : (int)refsize;
                    if (next > 0)
                    {
                        loadBuffer.Trigger(bufferSelect ? buffer1 : buffer0, next);
                    }

                    byte[] buffer = bufferSelect ? buffer0 : buffer1;

                    // Run hashes in parallel
                    Parallel.ForEach(hashers, Globals.ParallelOptions, h => h.Process(buffer, current));

                    // Wait for the load buffer worker, if needed
                    if (next > 0)
                    {
                        loadBuffer.Wait();
                    }

                    // Setup for the next hashing step
                    current      = next;
                    refsize     -= next;
                    bufferSelect = !bufferSelect;
                }

                // Finalize all hashing helpers
                loadBuffer.Finish();
                Parallel.ForEach(hashers, Globals.ParallelOptions, h => h.Terminate());

                // Get the results
                BaseFile baseFile = new BaseFile()
                {
                    Size    = size,
                    CRC     = hashes.HasFlag(Hash.CRC) ? hashers.First(h => h.HashType == Hash.CRC).GetHash() : null,
                    MD5     = hashes.HasFlag(Hash.MD5) ? hashers.First(h => h.HashType == Hash.MD5).GetHash() : null,
                    SHA1    = hashes.HasFlag(Hash.SHA1) ? hashers.First(h => h.HashType == Hash.SHA1).GetHash() : null,
                    SHA256  = hashes.HasFlag(Hash.SHA256) ? hashers.First(h => h.HashType == Hash.SHA256).GetHash() : null,
                    SHA384  = hashes.HasFlag(Hash.SHA384) ? hashers.First(h => h.HashType == Hash.SHA384).GetHash() : null,
                    SHA512  = hashes.HasFlag(Hash.SHA512) ? hashers.First(h => h.HashType == Hash.SHA512).GetHash() : null,
                    SpamSum = hashes.HasFlag(Hash.SpamSum) ? hashers.First(h => h.HashType == Hash.SpamSum).GetHash() : null,
                };

                // Dispose of the hashers
                loadBuffer.Dispose();
                hashers.ForEach(h => h.Dispose());

                return(baseFile);
            }
            catch (IOException ex)
            {
                LoggerImpl.Warning(ex, "An exception occurred during hashing.");
                return(new BaseFile());
            }
            finally
            {
                if (!keepReadOpen)
                {
                    input.Dispose();
                }
                else
                {
                    input.SeekIfPossible();
                }
            }
        }
예제 #4
0
        /// <summary>
        /// Populate the dictionary from an INI file
        /// </summary>
        /// <param name="ini">Path to INI file to populate from</param>
        /// <remarks>
        /// The INI file format that is supported here is not exactly the same
        /// as a traditional one. This expects a MAME extras format, which usually
        /// doesn't contain key value pairs and always at least contains one section
        /// called `ROOT_FOLDER`. If that's the name of a section, then we assume
        /// the value is boolean. If there's another section name, then that is set
        /// as the value instead.
        /// </remarks>
        public bool PopulateFromFile(string ini)
        {
            // Prepare all intenral variables
            IniReader ir = new IniReader(ini)
            {
                ValidateRows = false
            };
            bool foundRootFolder = false;

            // If we got a null reader, just return
            if (ir == null)
            {
                return(false);
            }

            // Otherwise, read the file to the end
            try
            {
                while (!ir.EndOfStream)
                {
                    // Read in the next line and process
                    ir.ReadNextLine();

                    // We don't care about whitespace or comments
                    if (ir.RowType == IniRowType.None || ir.RowType == IniRowType.Comment)
                    {
                        continue;
                    }

                    // If we have a section, just read it in
                    if (ir.RowType == IniRowType.SectionHeader)
                    {
                        // If we've found the start of the extras, set the flag
                        if (string.Equals(ir.Section, "ROOT_FOLDER", StringComparison.OrdinalIgnoreCase))
                        {
                            foundRootFolder = true;
                        }

                        continue;
                    }

                    // If we have a value, then we start populating the dictionary
                    else if (foundRootFolder)
                    {
                        // Get the value and machine name
                        string value       = ir.Section;
                        string machineName = ir.CurrentLine.Trim();

                        // If the section is "ROOT_FOLDER", then we use the value "true" instead.
                        // This is done because some INI files use the name of the file as the
                        // category to be assigned to the items included.
                        if (value == "ROOT_FOLDER")
                        {
                            value = "true";
                        }

                        // Add the new mapping
                        Mappings[machineName] = value;
                    }
                }
            }
            catch (Exception ex)
            {
                LoggerImpl.Warning(ex, $"Exception found while parsing '{ini}'");
                return(false);
            }

            ir.Dispose();
            return(true);
        }