/// <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> /// 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}"); } } }
/// <summary> /// Adds a directory to the list of directories. /// </summary> /// <param name="directory">The directory to add.</param> public void Add(GgpkDirectory directory) { this.directories.Add(directory); }