예제 #1
0
        /// <summary>
        /// Initializes a new instance of the <see cref="GgpkArchive"/> class.
        /// </summary>
        /// <param name="fileName">The ggpk archive file.</param>
        /// <exception cref="GgpkException">Error while reading the archive.</exception>
        internal GgpkArchive(string fileName)
        {
            IEnumerable <GgpkRecord> records    = GgpkRecords.From(fileName);
            GgpkMainRecord           mainRecord = records.OfType <GgpkMainRecord>().FirstOrDefault();

            this.RawRecords = records;
            this.root       = new GgpkDirectory();
            this.FileName   = fileName;

            if (mainRecord is null)
            {
                throw new GgpkException($"Error while analyzing the ggpk archive file: no record of type GGPK found");
            }

            // Convert references in main record (ulong) to type GgpkDirectoryRecordEntry required by BuildDirectoryTree-method
            IEnumerable <GgpkDirectoryRecordEntry> createdEntries = mainRecord.RecordOffsets.Select((offset) => new GgpkDirectoryRecordEntry()
            {
                Offset    = offset,
                TimeStamp = 0
            }).ToList();

            // Create record dictionary (which is /much/ faster than querying the IEnumerable with Linq)
            Dictionary <ulong, GgpkRecord> recordDictionary = new Dictionary <ulong, GgpkRecord>();

            foreach (GgpkRecord record in records)
            {
                recordDictionary.Add(record.Offset, record);
            }

            this.BuildDirectoryTree(null, createdEntries, recordDictionary);
        }
예제 #2
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);
        }