/// <summary> /// Recursivly creates a directory tree by traversing PDIR records. Adds FILE records to the current directory /// tree node. Recursivly traverses PDIR records and adds them to the current directory tree node's children. /// </summary> /// <param name="directoryEntry">Directory Entry</param> /// <param name="root">Parent node</param> private void BuildDirectoryTree(DirectoryRecord.DirectoryEntry directoryEntry, DirectoryTreeNode root) { if (!RecordOffsets.ContainsKey(directoryEntry.Offset)) { return; // TODO throw error } if (RecordOffsets[directoryEntry.Offset] is DirectoryRecord) { // This offset is a directory, add it as a child of root and process all of it's entries var currentDirectory = RecordOffsets[directoryEntry.Offset] as DirectoryRecord; _directories.Add(currentDirectory); var child = new DirectoryTreeNode { Name = currentDirectory.Name, Parent = root, Children = new List <DirectoryTreeNode>(), Files = new List <FileRecord>(), Record = currentDirectory, }; root.Children.Add(child); foreach (var entry in currentDirectory.Entries) { BuildDirectoryTree(entry, child); } } else if (RecordOffsets[directoryEntry.Offset] is FileRecord) { var currentFile = RecordOffsets[directoryEntry.Offset] as FileRecord; _files.Add(currentFile); currentFile.ContainingDirectory = root; root.Files.Add(currentFile); } }
/// <summary> /// Iterates through the pack file and records the offsets and headers of each record found /// </summary> /// <param name="pathToGgpk">Path to pack file to read</param> /// <param name="output">Output function</param> public void ReadRecordOffsets(string pathToGgpk, Action <string> output) { var previousPercentComplete = 0.0f; using (var fs = OpenFile(pathToGgpk, out _isReadOnly)) { var br = new BinaryReader(fs); var streamLength = br.BaseStream.Length; while (br.BaseStream.Position < streamLength) { var currentOffset = br.BaseStream.Position; var record = ReadRecord(br); RecordOffsets.Add(currentOffset, record); var percentComplete = currentOffset / (float)streamLength; if (percentComplete - previousPercentComplete >= 0.10f) { output?.Invoke(String.Format("\t{0:00.00}%{1}", 100.0 * percentComplete, Environment.NewLine)); previousPercentComplete = percentComplete; } } if (output != null) { var percentReady = 100.0f * br.BaseStream.Position / br.BaseStream.Length; output(String.Format("\t{0:00.00}%{1}", percentReady, Environment.NewLine)); } } }
public void DeserializeRecords(string pathToBin, Action<string> output) { if (output != null) { output(Environment.NewLine); output("Deserializing... "); } var Serialized = File.OpenRead(pathToBin); var s = new BinaryReader(Serialized); while (Serialized.Length - Serialized.Position > 1) { long offset = s.ReadInt64(); switch (s.ReadByte()) { case 1: RecordOffsets.Add(offset, new FileRecord(s.ReadInt64(), s.ReadUInt32(), s.ReadBytes(32), s.ReadString(), s.ReadInt64(), s.ReadInt64())); break; case 2: long recordBegin = s.ReadInt64(); uint length = s.ReadUInt32(); long[] recordOffsets = new long[s.ReadInt32()]; for (int i = 0; i < recordOffsets.Length; i++) { recordOffsets[i] = s.ReadInt64(); } RecordOffsets.Add(offset, new GgpkRecord(recordBegin, length, recordOffsets)); break; case 3: RecordOffsets.Add(offset, new FreeRecord(s.ReadUInt32(), s.ReadInt64(), s.ReadInt64())); break; case 4: long recordBegin2 = s.ReadInt64(); uint length2 = s.ReadUInt32(); byte[] hash = s.ReadBytes(32); string name = s.ReadString(); long entriesBegin = s.ReadInt64(); int entriesCount = s.ReadInt32(); var entries = new List<DirectoryRecord.DirectoryEntry>(entriesCount); for (int i = 0; i < entriesCount; i++) { entries.Add(new DirectoryRecord.DirectoryEntry { EntryNameHash = s.ReadUInt32(), Offset = s.ReadInt64(), }); } RecordOffsets.Add(offset, new DirectoryRecord(recordBegin2, length2, hash, name, entriesBegin, entries)); break; } } Serialized.Flush(); Serialized.Close(); output?.Invoke("Done!" + Environment.NewLine); }
/// <summary> /// Builds a linked list of FREE records by traversing FREE records in GGPK file /// </summary> /// <returns>Linked list containing list of FREE records</returns> private LinkedList <FreeRecord> BuildFreeList() { var ggpkRecord = RecordOffsets[0] as GgpkRecord; if (ggpkRecord == null) { throw new Exception("First record isn't GGPK record"); } // First level only contains a empty string name directory record and a free record var firstFreeRecordOffset = ggpkRecord.RecordOffsets .Where(RecordOffsets.ContainsKey).FirstOrDefault(o => RecordOffsets[o] is FreeRecord); if (firstFreeRecordOffset == 0) // default value { throw new Exception("Couldn't find first FREE record offset"); } var currentFreeRecord = RecordOffsets[firstFreeRecordOffset] as FreeRecord; if (currentFreeRecord == null) { throw new Exception("Couldn't find first FREE record"); } var freeList = new LinkedList <FreeRecord>(); while (true) { freeList.AddLast(currentFreeRecord); _freeRecords.Add(currentFreeRecord); var nextFreeOFfset = currentFreeRecord.NextFreeOffset; if (nextFreeOFfset == 0) { break; } if (!RecordOffsets.ContainsKey(nextFreeOFfset)) { throw new Exception("Failed to find next FREE record in map of record offsets"); } currentFreeRecord = RecordOffsets[currentFreeRecord.NextFreeOffset] as FreeRecord; if (currentFreeRecord == null) { throw new Exception("Found a record that wasn't a FREE record while looking for next FREE record"); } } return(freeList); }
public void ReplaceFile(FileRecord fileRecord, byte[] data) { using (var bw = new BinaryWriter(File.Open(_pathToGppk, FileMode.Open))) { RecordOffsets.Remove(fileRecord.RecordBegin); _freeRecordManager.AddFromFileRecord(bw, fileRecord); fileRecord.Data = data; long position = _freeRecordManager.AllocSpace(bw, fileRecord.Length); bw.BaseStream.Position = position; fileRecord.WriteData(bw); if (!fileRecord.ContainingDirectory.Record.UpdateOffset(bw, fileRecord.GetNameHash(), fileRecord.RecordBegin)) { throw new ApplicationException("Entry not found!"); } RecordOffsets[fileRecord.RecordBegin] = fileRecord; } }