示例#1
0
        /// <summary>
        /// Reads the given ggpk archive <paramref name="stream"/> and returns all records.
        /// </summary>
        /// <param name="stream">The ggpk <see cref="Stream"/>.</param>
        /// <returns>All records read from the <see cref="Stream"/>.</returns>
        /// <exception cref="ArgumentNullException"><c>stream</c> is <c>null</c>.</exception>
        /// <exception cref="GgpkException">Error while reading the archive.</exception>
        public static IEnumerable <GgpkRecord> From(Stream stream)
        {
            if (stream is null)
            {
                throw new ArgumentNullException(nameof(stream));
            }

            List <GgpkRecord> records = new List <GgpkRecord>();

            using (BinaryReader ggpkStreamReader = new BinaryReader(stream))
            {
                try
                {
                    while (ggpkStreamReader.BaseStream.Position < ggpkStreamReader.BaseStream.Length)
                    {
                        GgpkRecordMarker recordMarker  = GgpkRecordMarker.From(ggpkStreamReader);
                        GgpkRecord       currentRecord = null;

                        if (recordMarker.Offset + recordMarker.Length > (ulong)ggpkStreamReader.BaseStream.Length)
                        {
                            throw new InvalidDataException($"Invalid record length {recordMarker.Length} at offset {recordMarker.Offset}");
                        }

                        switch (recordMarker.Type)
                        {
                        case "GGPK":
                            currentRecord = GgpkMainRecord.From(ggpkStreamReader);
                            break;

                        case "FREE":
                            currentRecord = GgpkFreeRecord.From(recordMarker, ggpkStreamReader);
                            break;

                        case "FILE":
                            currentRecord = GgpkFileRecord.From(recordMarker, ggpkStreamReader);
                            break;

                        case "PDIR":
                            currentRecord = GgpkDirectoryRecord.From(ggpkStreamReader);
                            break;

                        default:
                            throw new InvalidDataException($"Unknown record type {recordMarker.Type} at offset {recordMarker.Offset}");
                        }

                        currentRecord.Offset = recordMarker.Offset;
                        currentRecord.Length = recordMarker.Length;

                        records.Add(currentRecord);
                    }
                }
                catch (Exception ex)
                {
                    throw new GgpkException($"Error while parsing the ggpk archive file", ex)
                          {
                              Offset = ggpkStreamReader.BaseStream.Position
                          };
                }
            }

            return(records);
        }
示例#2
0
        /// <summary>
        /// Builds the directory and file tree.
        /// </summary>
        /// <param name="currentDirectory">The current directory.</param>
        /// <param name="currentDirectoryContent">The desired content of the current directory.</param>
        /// <param name="records">All <see cref="GgpkRecord">records</see> from the ggpk archive used to find the references.</param>
        private void BuildDirectoryTree(
            GgpkDirectory currentDirectory,
            IEnumerable <GgpkDirectoryRecordEntry> currentDirectoryContent,
            IDictionary <ulong, GgpkRecord> records)
        {
            foreach (GgpkDirectoryRecordEntry recordEntry in currentDirectoryContent)
            {
                GgpkRecord referencedRecord = records[recordEntry.Offset];

                if (referencedRecord is null)
                {
                    throw new GgpkException($"Error while analyzing the ggpk archive file: no element found at offset {recordEntry.Offset}");
                }

                switch (referencedRecord)
                {
                case GgpkDirectoryRecord directoryRecord:

                    GgpkDirectory directory = new GgpkDirectory()
                    {
                        Name            = directoryRecord.DirectoryName,
                        TimeStamp       = recordEntry.TimeStamp,
                        Hash            = directoryRecord.Hash,
                        Parent          = currentDirectory,
                        ArchiveFileName = this.FileName
                    };

                    if (currentDirectory != null)
                    {
                        currentDirectory.Add(directory);
                    }
                    else
                    {
                        this.root = directory;
                    }

                    this.BuildDirectoryTree(directory, directoryRecord.Entries, records);

                    break;

                case GgpkFileRecord fileRecord:

                    GgpkFile file = new GgpkFile()
                    {
                        Name            = fileRecord.FileName,
                        TimeStamp       = recordEntry.TimeStamp,
                        Hash            = fileRecord.Hash,
                        Offset          = fileRecord.FileOffset,
                        Length          = fileRecord.FileLength,
                        Parent          = currentDirectory,
                        ArchiveFileName = this.FileName
                    };

                    currentDirectory.Add(file);

                    break;

                case GgpkFreeRecord freeRecord:
                    break;

                default:
                    throw new GgpkException($"Error while analyzing the ggpk archive file: no element of type directory or file found at offset {recordEntry.Offset}");
                }
            }
        }