/// <summary>
        /// Read the next entry.
        /// </summary>
        /// <returns>The read DBEntry if any; null otherwise.</returns>
        public DBEntry Read()
        {
            switch (state)
            {
            case ReaderState.EndOfFile:
            {
                return(null);
            }

            case ReaderState.ReadHeaderNext:
            {
                LastReadEntry = Header = DBHeader.ReadFrom(binaryReader, strBuilder);

                state = ReaderState.ReadRootDirectoryNext;

                return(LastReadEntry);
            }

            case ReaderState.ReadRootDirectoryNext:
            {
                positionBeforeReadRootDirectory = inputStream.Position;

                LastReadEntry = currentRootDirectory = DBRootDirectoryEntry.ReadFrom(binaryReader, strBuilder);

                if (currentRootDirectory == null)
                {
                    state = ReaderState.EndOfFile;
                    return(null);
                }

                currentPathStack.Push(currentPath = currentRootDirectory.Path);

                currentRootDirectory.FullName = currentPath;

                state = ReaderState.ReadFileOrDirectoryNext;

                return(LastReadEntry);
            }

            case ReaderState.ReadFileOrDirectoryNext:
            {
                typeAndAttributes = (DBEntryAttributes)binaryReader.ReadByte();

                if (typeAndAttributes != 0 || depth > 0)
                {
                    if (typeAndAttributes.HasFlag(DBEntryAttributes.Directory))
                    {
                        positionBeforeReadDirectory = inputStream.Position;

                        var directoryEntry = DBDirectoryEntry.ReadFrom(binaryReader, typeAndAttributes, strBuilder);

                        currentPath += (depth == 0 ? string.Empty : @"\") + directoryEntry.DirectoryName;

                        directoryEntry.ParentDirectory = depth == 0 ? null : dirStack.Peek();
                        directoryEntry.RootDirectory   = currentRootDirectory;
                        directoryEntry.FullName        = currentPath;

                        depth++;

                        dirStack.Push(directoryEntry);

                        return(LastReadEntry = directoryEntry);
                    }

                    if (typeAndAttributes != 0)
                    {
                        var fileEntry = DBFileEntry.ReadFrom(binaryReader, typeAndAttributes, strBuilder);

                        fileEntry.ParentDirectory = depth == 0 ? null : dirStack.Peek();
                        fileEntry.RootDirectory   = currentRootDirectory;
                        fileEntry.FullName        = currentPath + (depth == 0 ? string.Empty : @"\") + fileEntry.FileName;

                        return(LastReadEntry = fileEntry);
                    }

                    depth--;
                    var poppedDir = dirStack.Pop();

                    currentPath = poppedDir.ParentDirectory?.FullName ?? poppedDir.RootDirectory?.FullName ?? string.Empty;

                    return(Read());
                }

                var nullByte = binaryReader.ReadByte();     // normally its a dword byte but we should already have read a zero byte before

                if (nullByte != 0)
                {
                    throw new ArchiveException($"Was expecting to read a final null-byte, read {nullByte} instead.");
                }

                currentPathStack.Pop();

                state = ReaderState.ReadRootDirectoryNext;

                return(Read());
            }
            }

            LastReadEntry = null;
            return(null);
        }
Exemple #2
0
        public IEnumerable <DBEntry> EnumerateEntries(IDBEntryFilter filter)
        {
            var strBuilder = new StringBuilder(255);

            var header    = DBHeader.ReadFrom(binaryReader, strBuilder);
            var hasFilter = filter != null;

            // Always return the header as the first entry
            yield return(header);

            // var positionBeforeRead = inputStream.Position;

            var positionBeforeReadRootDirectory = inputStream.Position;
            var currentRootDirectory            = DBRootDirectoryEntry.ReadFrom(binaryReader, strBuilder);

            // var readLength = inputStream.Position - positionBeforeRead;

            int depth            = 0;
            var dirs             = new Stack <DBDirectoryEntry>();
            var currentPathStack = new Stack <string>(64);

            while (currentRootDirectory != null)
            {
                string currentPath;
                currentPathStack.Push(currentPath = currentRootDirectory.Path);

                currentRootDirectory.FullName = currentPath;
                var filterResult = filter?.Filter(currentRootDirectory) ?? DBEntryFilterResultActions.ExcludeNothing;

                if (!hasFilter || (filterResult == DBEntryFilterResultActions.ExcludeNothing ||
                                   !filterResult.HasFlag(DBEntryFilterResultActions.ExcludeSelf)))
                {
                    yield return(currentRootDirectory);
                }

                if (hasFilter && filterResult.HasFlag(DBEntryFilterResultActions.ExcludeChildren))
                {
                    // Skip the rest
                    inputStream.Position += (positionBeforeReadRootDirectory - inputStream.Position) + currentRootDirectory.DataLength + 4;

                    currentPathStack.Pop();

                    positionBeforeReadRootDirectory = inputStream.Position;
                    currentRootDirectory            = DBRootDirectoryEntry.ReadFrom(binaryReader, strBuilder);

                    continue;
                }

                var typeAndAttributes = (DBEntryAttributes)binaryReader.ReadByte();

                while (typeAndAttributes != 0 || depth > 0)
                {
                    if (typeAndAttributes.HasFlag(DBEntryAttributes.Directory))
                    {
                        var positionBeforeReadDirectory = inputStream.Position;

                        var directoryEntry = DBDirectoryEntry.ReadFrom(binaryReader, typeAndAttributes, strBuilder);

                        currentPath += (depth == 0 ? string.Empty : @"\") + directoryEntry.DirectoryName;

                        directoryEntry.ParentDirectory = depth == 0 ? null : dirs.Peek();
                        directoryEntry.RootDirectory   = currentRootDirectory;
                        directoryEntry.FullName        = currentPath;

                        filterResult = filter?.Filter(directoryEntry) ?? DBEntryFilterResultActions.ExcludeNothing;

                        if (!hasFilter || (filterResult == DBEntryFilterResultActions.ExcludeNothing ||
                                           !filterResult.HasFlag(DBEntryFilterResultActions.ExcludeSelf)))
                        {
                            yield return(directoryEntry);
                        }

                        if (hasFilter && filterResult.HasFlag(DBEntryFilterResultActions.ExcludeChildren))
                        {
                            // Skip the rest
                            inputStream.Position += (positionBeforeReadDirectory - inputStream.Position) + directoryEntry.DataLength;

                            var poppedDir = dirs.Count > 0 ? dirs.Peek() : null;

                            currentPath = poppedDir?.ParentDirectory?.FullName ?? directoryEntry?.RootDirectory?.FullName ?? string.Empty;
                        }
                        else
                        {
                            depth++;

                            dirs.Push(directoryEntry);
                        }
                    }
                    else if (typeAndAttributes != 0)
                    {
                        var fileEntry = DBFileEntry.ReadFrom(binaryReader, typeAndAttributes, strBuilder);

                        fileEntry.ParentDirectory = depth == 0 ? null : dirs.Peek();
                        fileEntry.RootDirectory   = currentRootDirectory;
                        fileEntry.FullName        = currentPath + (depth == 0 ? string.Empty : @"\") + fileEntry.FileName;

                        if (!hasFilter || (filterResult == DBEntryFilterResultActions.ExcludeNothing ||
                                           !filterResult.HasFlag(DBEntryFilterResultActions.ExcludeSelf)))
                        {
                            yield return(fileEntry);
                        }
                    }
                    else
                    {
                        depth--;
                        var poppedDir = dirs.Pop();

                        currentPath = poppedDir.ParentDirectory?.FullName ?? poppedDir.RootDirectory?.FullName ?? string.Empty;
                    }

                    typeAndAttributes = (DBEntryAttributes)binaryReader.ReadByte();
                }

                var nullByte = binaryReader.ReadByte(); // normally its a dword byte but we should already have read a zero byte before

                if (nullByte != 0)
                {
                    throw new ArchiveException($"Was expecting to read a final null-byte, read {nullByte} instead.");
                }

                // positionBeforeRead = inputStream.Position;

                currentPathStack.Pop();

                positionBeforeReadRootDirectory = inputStream.Position;
                currentRootDirectory            = DBRootDirectoryEntry.ReadFrom(binaryReader, strBuilder);

                // readLength = inputStream.Position - positionBeforeRead;
            }
        }