private int AssignOffsets(StringNode node, int offset, IDictionary <StringNode, int> nodeOffsetMap) { foreach (var internalNode in node.Nodes) { offset = AssignOffsets(internalNode, offset, nodeOffsetMap); if (!string.IsNullOrEmpty(node.Text)) { var sameNode = nodeOffsetMap.Keys.FirstOrDefault(x => x.Text == node.Text); nodeOffsetMap[node] = sameNode != null ? nodeOffsetMap[sameNode] : offset; if (sameNode == null) { offset += node.Text.Length + 1; } } } if (!string.IsNullOrEmpty(node.Text)) { var sameNode = nodeOffsetMap.Keys.FirstOrDefault(x => x.Text == node.Text); nodeOffsetMap[node] = sameNode != null ? nodeOffsetMap[sameNode] : offset; if (sameNode == null) { offset += node.Text.Length + 1; } } return(offset); }
private void WriteNodes(StringNode node, BinaryWriterX bw, IDictionary <StringNode, int> nodeOffsetMap, ref int fileId) { bw.Write((short)node.Nodes.Count); var nextPosition = bw.BaseStream.Position + node.Nodes.Count * 4; foreach (var internalNode in node.Nodes) { var currentPosition = bw.BaseStream.Position; var isEnd = internalNode.Nodes.Count <= 0; var flags = isEnd ? (fileId++ << 1) | 1 : nextPosition << 1; if (internalNode.Nodes.Count > 0) { bw.BaseStream.Position = nextPosition; WriteNodes(internalNode, bw, nodeOffsetMap, ref fileId); nextPosition = bw.BaseStream.Position; } if (currentPosition == 0x18C) { Debugger.Break(); } bw.BaseStream.Position = currentPosition; bw.Write((short)nodeOffsetMap[internalNode]); bw.Write((short)flags); } bw.BaseStream.Position = nextPosition; }
public void Save(Stream output, IList <IArchiveFileInfo> files) { using var bw = new BinaryWriterX(output); // Calculate offsets var nameOffset = 8; // Build string tree var fileNames = files.Select(x => x.FilePath.ToRelative().FullName).ToArray(); var rootNode = new StringNode(); rootNode.AddRange(fileNames); // Assign offsets to nodes var nodeOffsetMap = new Dictionary <StringNode, int>(); var nameTableOffset = AssignOffsets(rootNode, nameOffset, nodeOffsetMap); nameTableOffset = (nameTableOffset + 1) & ~1; // Write node tree output.Position = nameTableOffset; var fileId = 0; WriteNodes(rootNode, bw, nodeOffsetMap, ref fileId); var entryOffset = bw.BaseStream.Length; var fileOffset = entryOffset + files.Count * EntrySize; // Write files var entries = new List <PakEntry>(); var filePosition = fileOffset; foreach (var file in files.Cast <ArchiveFileInfo>()) { output.Position = filePosition; var writtenSize = file.SaveFileData(output); entries.Add(new PakEntry { offset = (int)filePosition, size = (int)writtenSize }); filePosition += writtenSize; } // Write entries output.Position = entryOffset; bw.WriteMultiple(entries); // Write strings foreach (var pair in nodeOffsetMap) { output.Position = pair.Value; bw.WriteString(pair.Key.Text, Encoding.ASCII, false); } // Write header output.Position = 0; bw.WriteType(new PakHeader { fileCount = (short)files.Count, entryOffset = (int)entryOffset, nameTable = (short)nameTableOffset }); }
private void Add(string input, int position) { // If no nodes exist yet, add substring without processing it if (Nodes.Count < 0) { Nodes.Add(new StringNode { Text = input.Substring(position, input.Length - position) }); return; } // Determine best matching node StringNode matchingNode = null; foreach (var node in Nodes) { if (node.Text[0] == input[position]) { matchingNode = node; break; } } // If no node matched, create a new one with substring if (matchingNode == null) { Nodes.Add(new StringNode { Text = input.Substring(position, input.Length - position) }); return; } // Match node until end or difference var length = Math.Min(matchingNode.Text.Length, input.Length - position); for (var i = 1; i < length; i++) { if (input[position + i] != matchingNode.Text[i]) { // Split and relocate nodes var splitNode = new StringNode() { Text = matchingNode.Text.Substring(i, matchingNode.Text.Length - i) }; var newNode = new StringNode() { Text = input.Substring(position + i, input.Length - position - i) }; matchingNode.Text = matchingNode.Text.Substring(0, i); splitNode.Nodes = matchingNode.Nodes; matchingNode.Nodes = new List <StringNode> { splitNode, newNode }; return; } } // If strings only differed in length if (matchingNode.Text.Length < input.Length - position) { matchingNode.Add(input, position + matchingNode.Text.Length); } if (input.Length - position < matchingNode.Text.Length) { var splitNode = new StringNode() { Text = matchingNode.Text.Substring(input.Length - position, matchingNode.Text.Length - (input.Length - position)) }; matchingNode.Text = matchingNode.Text.Substring(0, input.Length - position); splitNode.Nodes = matchingNode.Nodes; matchingNode.Nodes = new List <StringNode> { splitNode }; } }