Broadcom devices .trx firmware packaging: C# parsing library

.trx file format is widely used for distribution of firmware updates for Broadcom devices. The most well-known are ASUS routers.

Fundamentally, it includes a footer which acts as a safeguard against installing a firmware package on a wrong hardware model or version, and a header which list numerous partitions packaged inside a single .trx file.

trx files not necessarily contain all these headers.

File extension

trx

KS implementation details

License: GPL-2.0-or-later
Minimal Kaitai Struct required: 0.9

This page hosts a formal specification of Broadcom devices .trx firmware packaging using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Usage

Runtime library

All parsing code for C# generated by Kaitai Struct depends on the C# runtime library. You have to install it before you can parse data.

The C# runtime library is available in the NuGet Gallery. Installation instructions can also be found there.

Code

Parse a local file and get structure in memory:

var data = BroadcomTrx.FromFile("path/to/local/file.trx");

Or parse structure from a byte array:

byte[] someArray = new byte[] { ... };
var data = new BroadcomTrx(new KaitaiStream(someArray));

After that, one can get various attributes from the structure by accessing properties like:

data.Header // => get header

C# source code to parse Broadcom devices .trx firmware packaging

BroadcomTrx.cs

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

using System.Collections.Generic;

namespace Kaitai
{

    /// <summary>
    /// .trx file format is widely used for distribution of firmware
    /// updates for Broadcom devices. The most well-known are ASUS routers.
    /// 
    /// Fundamentally, it includes a footer which acts as a safeguard
    /// against installing a firmware package on a wrong hardware model or
    /// version, and a header which list numerous partitions packaged inside
    /// a single .trx file.
    /// 
    /// trx files not necessarily contain all these headers.
    /// </summary>
    /// <remarks>
    /// Reference: <a href="https://github.com/openwrt/firmware-utils/blob/a2c80c5/src/trx.c">Source</a>
    /// </remarks>
    /// <remarks>
    /// Reference: <a href="https://web.archive.org/web/20190127154419/https://openwrt.org/docs/techref/header">Source</a>
    /// </remarks>
    /// <remarks>
    /// Reference: <a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/mtd/partitions/brcm,trx.txt">Source</a>
    /// </remarks>
    public partial class BroadcomTrx : KaitaiStruct
    {
        public static BroadcomTrx FromFile(string fileName)
        {
            return new BroadcomTrx(new KaitaiStream(fileName));
        }

        public BroadcomTrx(KaitaiStream p__io, KaitaiStruct p__parent = null, BroadcomTrx p__root = null) : base(p__io)
        {
            m_parent = p__parent;
            m_root = p__root ?? this;
            f_header = false;
            f_tail = false;
            _read();
        }
        private void _read()
        {
        }
        public partial class Revision : KaitaiStruct
        {
            public static Revision FromFile(string fileName)
            {
                return new Revision(new KaitaiStream(fileName));
            }

            public Revision(KaitaiStream p__io, BroadcomTrx.Tail.HwCompInfo p__parent = null, BroadcomTrx p__root = null) : base(p__io)
            {
                m_parent = p__parent;
                m_root = p__root;
                _read();
            }
            private void _read()
            {
                _major = m_io.ReadU1();
                _minor = m_io.ReadU1();
            }
            private byte _major;
            private byte _minor;
            private BroadcomTrx m_root;
            private BroadcomTrx.Tail.HwCompInfo m_parent;
            public byte Major { get { return _major; } }
            public byte Minor { get { return _minor; } }
            public BroadcomTrx M_Root { get { return m_root; } }
            public BroadcomTrx.Tail.HwCompInfo M_Parent { get { return m_parent; } }
        }
        public partial class Version : KaitaiStruct
        {
            public static Version FromFile(string fileName)
            {
                return new Version(new KaitaiStream(fileName));
            }

            public Version(KaitaiStream p__io, BroadcomTrx.Tail p__parent = null, BroadcomTrx p__root = null) : base(p__io)
            {
                m_parent = p__parent;
                m_root = p__root;
                _read();
            }
            private void _read()
            {
                _major = m_io.ReadU1();
                _minor = m_io.ReadU1();
                _patch = m_io.ReadU1();
                _tweak = m_io.ReadU1();
            }
            private byte _major;
            private byte _minor;
            private byte _patch;
            private byte _tweak;
            private BroadcomTrx m_root;
            private BroadcomTrx.Tail m_parent;
            public byte Major { get { return _major; } }
            public byte Minor { get { return _minor; } }
            public byte Patch { get { return _patch; } }
            public byte Tweak { get { return _tweak; } }
            public BroadcomTrx M_Root { get { return m_root; } }
            public BroadcomTrx.Tail M_Parent { get { return m_parent; } }
        }

        /// <summary>
        /// A safeguard against installation of firmware to an incompatible device
        /// </summary>
        public partial class Tail : KaitaiStruct
        {
            public static Tail FromFile(string fileName)
            {
                return new Tail(new KaitaiStream(fileName));
            }

            public Tail(KaitaiStream p__io, BroadcomTrx p__parent = null, BroadcomTrx p__root = null) : base(p__io)
            {
                m_parent = p__parent;
                m_root = p__root;
                _read();
            }
            private void _read()
            {
                _version = new Version(m_io, this, m_root);
                _productId = System.Text.Encoding.GetEncoding("utf-8").GetString(KaitaiStream.BytesTerminate(m_io.ReadBytes(12), 0, false));
                _compHw = new List<HwCompInfo>();
                for (var i = 0; i < 4; i++)
                {
                    _compHw.Add(new HwCompInfo(m_io, this, m_root));
                }
                _reserved = m_io.ReadBytes(32);
            }
            public partial class HwCompInfo : KaitaiStruct
            {
                public static HwCompInfo FromFile(string fileName)
                {
                    return new HwCompInfo(new KaitaiStream(fileName));
                }

                public HwCompInfo(KaitaiStream p__io, BroadcomTrx.Tail p__parent = null, BroadcomTrx p__root = null) : base(p__io)
                {
                    m_parent = p__parent;
                    m_root = p__root;
                    _read();
                }
                private void _read()
                {
                    _min = new Revision(m_io, this, m_root);
                    _max = new Revision(m_io, this, m_root);
                }
                private Revision _min;
                private Revision _max;
                private BroadcomTrx m_root;
                private BroadcomTrx.Tail m_parent;
                public Revision Min { get { return _min; } }
                public Revision Max { get { return _max; } }
                public BroadcomTrx M_Root { get { return m_root; } }
                public BroadcomTrx.Tail M_Parent { get { return m_parent; } }
            }
            private Version _version;
            private string _productId;
            private List<HwCompInfo> _compHw;
            private byte[] _reserved;
            private BroadcomTrx m_root;
            private BroadcomTrx m_parent;

            /// <summary>
            /// 1.9.2.7 by default
            /// </summary>
            public Version Version { get { return _version; } }
            public string ProductId { get { return _productId; } }

            /// <summary>
            /// 0.02 - 2.99
            /// </summary>
            public List<HwCompInfo> CompHw { get { return _compHw; } }
            public byte[] Reserved { get { return _reserved; } }
            public BroadcomTrx M_Root { get { return m_root; } }
            public BroadcomTrx M_Parent { get { return m_parent; } }
        }
        public partial class Header : KaitaiStruct
        {
            public static Header FromFile(string fileName)
            {
                return new Header(new KaitaiStream(fileName));
            }

            public Header(KaitaiStream p__io, BroadcomTrx p__parent = null, BroadcomTrx p__root = null) : base(p__io)
            {
                m_parent = p__parent;
                m_root = p__root;
                _read();
            }
            private void _read()
            {
                _magic = m_io.ReadBytes(4);
                if (!((KaitaiStream.ByteArrayCompare(Magic, new byte[] { 72, 68, 82, 48 }) == 0)))
                {
                    throw new ValidationNotEqualError(new byte[] { 72, 68, 82, 48 }, Magic, M_Io, "/types/header/seq/0");
                }
                _len = m_io.ReadU4le();
                _crc32 = m_io.ReadU4le();
                _version = m_io.ReadU2le();
                _flags = new Flags(m_io, this, m_root);
                _partitions = new List<Partition>();
                {
                    var i = 0;
                    Partition M_;
                    do {
                        M_ = new Partition(i, m_io, this, m_root);
                        _partitions.Add(M_);
                        i++;
                    } while (!( ((i >= 4) || (!(M_.IsPresent))) ));
                }
            }
            public partial class Partition : KaitaiStruct
            {
                public Partition(byte p_idx, KaitaiStream p__io, BroadcomTrx.Header p__parent = null, BroadcomTrx p__root = null) : base(p__io)
                {
                    m_parent = p__parent;
                    m_root = p__root;
                    _idx = p_idx;
                    f_isPresent = false;
                    f_isLast = false;
                    f_lenBody = false;
                    f_body = false;
                    _read();
                }
                private void _read()
                {
                    _ofsBody = m_io.ReadU4le();
                }
                private bool f_isPresent;
                private bool _isPresent;
                public bool IsPresent
                {
                    get
                    {
                        if (f_isPresent)
                            return _isPresent;
                        _isPresent = (bool) (OfsBody != 0);
                        f_isPresent = true;
                        return _isPresent;
                    }
                }
                private bool f_isLast;
                private bool? _isLast;
                public bool? IsLast
                {
                    get
                    {
                        if (f_isLast)
                            return _isLast;
                        if (IsPresent) {
                            _isLast = (bool) ( ((Idx == (M_Parent.Partitions.Count - 1)) || (!(M_Parent.Partitions[(Idx + 1)].IsPresent))) );
                        }
                        f_isLast = true;
                        return _isLast;
                    }
                }
                private bool f_lenBody;
                private int? _lenBody;
                public int? LenBody
                {
                    get
                    {
                        if (f_lenBody)
                            return _lenBody;
                        if (IsPresent) {
                            _lenBody = (int) ((IsLast ? (M_Root.M_Io.Size - OfsBody) : M_Parent.Partitions[(Idx + 1)].OfsBody));
                        }
                        f_lenBody = true;
                        return _lenBody;
                    }
                }
                private bool f_body;
                private byte[] _body;
                public byte[] Body
                {
                    get
                    {
                        if (f_body)
                            return _body;
                        if (IsPresent) {
                            KaitaiStream io = M_Root.M_Io;
                            long _pos = io.Pos;
                            io.Seek(OfsBody);
                            _body = io.ReadBytes(LenBody);
                            io.Seek(_pos);
                            f_body = true;
                        }
                        return _body;
                    }
                }
                private uint _ofsBody;
                private byte _idx;
                private BroadcomTrx m_root;
                private BroadcomTrx.Header m_parent;
                public uint OfsBody { get { return _ofsBody; } }
                public byte Idx { get { return _idx; } }
                public BroadcomTrx M_Root { get { return m_root; } }
                public BroadcomTrx.Header M_Parent { get { return m_parent; } }
            }
            public partial class Flags : KaitaiStruct
            {
                public static Flags FromFile(string fileName)
                {
                    return new Flags(new KaitaiStream(fileName));
                }

                public Flags(KaitaiStream p__io, BroadcomTrx.Header p__parent = null, BroadcomTrx p__root = null) : base(p__io)
                {
                    m_parent = p__parent;
                    m_root = p__root;
                    _read();
                }
                private void _read()
                {
                    _flags = new List<bool>();
                    for (var i = 0; i < 16; i++)
                    {
                        _flags.Add(m_io.ReadBitsIntLe(1) != 0);
                    }
                }
                private List<bool> _flags;
                private BroadcomTrx m_root;
                private BroadcomTrx.Header m_parent;
                public List<bool> Flags { get { return _flags; } }
                public BroadcomTrx M_Root { get { return m_root; } }
                public BroadcomTrx.Header M_Parent { get { return m_parent; } }
            }
            private byte[] _magic;
            private uint _len;
            private uint _crc32;
            private ushort _version;
            private Flags _flags;
            private List<Partition> _partitions;
            private BroadcomTrx m_root;
            private BroadcomTrx m_parent;
            public byte[] Magic { get { return _magic; } }

            /// <summary>
            /// Length of file including header
            /// </summary>
            public uint Len { get { return _len; } }

            /// <summary>
            /// CRC from `version` (??? todo: see the original and disambiguate) to end of file
            /// </summary>
            public uint Crc32 { get { return _crc32; } }
            public ushort Version { get { return _version; } }
            public Flags Flags { get { return _flags; } }

            /// <summary>
            /// Offsets of partitions from start of header
            /// </summary>
            public List<Partition> Partitions { get { return _partitions; } }
            public BroadcomTrx M_Root { get { return m_root; } }
            public BroadcomTrx M_Parent { get { return m_parent; } }
        }
        private bool f_header;
        private Header _header;
        public Header Header
        {
            get
            {
                if (f_header)
                    return _header;
                long _pos = m_io.Pos;
                m_io.Seek(0);
                _header = new Header(m_io, this, m_root);
                m_io.Seek(_pos);
                f_header = true;
                return _header;
            }
        }
        private bool f_tail;
        private Tail _tail;
        public Tail Tail
        {
            get
            {
                if (f_tail)
                    return _tail;
                long _pos = m_io.Pos;
                m_io.Seek((M_Io.Size - 64));
                _tail = new Tail(m_io, this, m_root);
                m_io.Seek(_pos);
                f_tail = true;
                return _tail;
            }
        }
        private BroadcomTrx m_root;
        private KaitaiStruct m_parent;
        public BroadcomTrx M_Root { get { return m_root; } }
        public KaitaiStruct M_Parent { get { return m_parent; } }
    }
}