public IList <IArchiveFileInfo> Load(Stream input) { using var br = new BinaryReaderX(input, true, ByteOrder.BigEndian); // Read node tree _root = FabNode.Read(br); // Read files var result = new List <IArchiveFileInfo>(); foreach (var fileBranch in _root.Nodes.Where(x => x.SubType == "FILE")) { var fileName = fileBranch.Nodes.FirstOrDefault(x => x.Type == "NAME")?.AsString(); if (result.Any(x => x.FilePath.ToRelative().FullName == fileName)) { continue; } var fileDataNode = fileBranch.Nodes.FirstOrDefault(x => x.SubType == "DATA"); var userNode = fileDataNode?.Nodes.FirstOrDefault(x => x.Type == "USER"); var relevantNode = userNode ?? fileDataNode; var fileStream = relevantNode?.Data; if (userNode?.SubType == "LZ4C") { result.Add(new FabArchiveFileInfo(fileStream, fileName, Compressions.Lz4Headerless, PeekDecompressedLength(fileStream)) { DataNode = relevantNode }); } else { result.Add(new FabArchiveFileInfo(fileStream, fileName) { DataNode = relevantNode }); } } return(result); }
public static FabNode Read(BinaryReaderX br) { // Read header var header = br.ReadType <FabNodeHeader>(); var result = new FabNode { _header = header }; switch (header.magic) { case "FBRC": switch (header.description) { case "BNDL": var numNode = Read(br); var count = numNode.AsInt32(); result.Nodes.Add(numNode); for (var i = 0; i < count; i++) { result.Nodes.Add(Read(br)); } break; case "TXTR": result.Nodes.Add(Read(br)); result.Nodes.Add(Read(br)); result.Nodes.Add(Read(br)); break; } break; case "USER": var offset1 = br.BaseStream.Position; br.BaseStream.Position += (header.size - 4 + 1) & ~1; result.Data = new SubStream(br.BaseStream, offset1, header.size - 4); break; case "LIST": switch (header.description) { case "FILE": result.Nodes.Add(Read(br)); result.Nodes.Add(Read(br)); break; case "META": result.Nodes.Add(Read(br)); result.Nodes.Add(Read(br)); result.Nodes.Add(Read(br)); result.Nodes.Add(Read(br)); result.Nodes.Add(Read(br)); break; case "DATA": var nextHeader = br.ReadType <FabNodeHeader>(); br.BaseStream.Position -= 0xC; // Specially handle USER node to unwrap the "actual" file data from it if (nextHeader.magic == "USER") { result.Nodes.Add(Read(br)); } else { var offset2 = br.BaseStream.Position; br.BaseStream.Position += (header.size - 4 + 1) & ~1; result.Data = new SubStream(br.BaseStream, offset2, header.size - 4); } break; } break; // By default treat content as data default: var offset = br.BaseStream.Position - 4; br.BaseStream.Position += (header.size - 4 + 1) & ~1; result._headerLength = 0x8; result.Data = new SubStream(br.BaseStream, offset, header.size); break; } return(result); }