Example #1
0
        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);
        }
Example #2
0
        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);
        }