/// <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); }
/// <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); }