.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.
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.
All parsing code for C++98/STL generated by Kaitai Struct depends on the C++/STL runtime library. You have to install it before you can parse data.
For C++, the easiest way is to clone the runtime library sources and build them along with your project.
Using Kaitai Struct in C++/STL usually consists of 3 steps.
std::istream
). One can open local file for that, or use existing std::string
or char*
buffer.
#include <fstream>
std::ifstream is("path/to/local/file.trx", std::ifstream::binary);
#include <sstream>
std::istringstream is(str);
#include <sstream>
const char buf[] = { ... };
std::string str(buf, sizeof buf);
std::istringstream is(str);
#include "kaitai/kaitaistream.h"
kaitai::kstream ks(&is);
broadcom_trx_t data(&ks);
After that, one can get various attributes from the structure by invoking getter methods like:
data.header() // => get header
#ifndef BROADCOM_TRX_H_
#define BROADCOM_TRX_H_
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
#include "kaitai/kaitaistruct.h"
#include <stdint.h>
#include <vector>
#if KAITAI_STRUCT_VERSION < 9000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
#endif
/**
* .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.
* \sa https://github.com/openwrt/firmware-utils/blob/a2c80c5/src/trx.c Source
* \sa https://web.archive.org/web/20190127154419/https://openwrt.org/docs/techref/header Source
* \sa https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/mtd/partitions/brcm,trx.txt Source
*/
class broadcom_trx_t : public kaitai::kstruct {
public:
class revision_t;
class version_t;
class tail_t;
class header_t;
broadcom_trx_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, broadcom_trx_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~broadcom_trx_t();
class revision_t : public kaitai::kstruct {
public:
revision_t(kaitai::kstream* p__io, broadcom_trx_t::tail_t::hw_comp_info_t* p__parent = 0, broadcom_trx_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~revision_t();
private:
uint8_t m_major;
uint8_t m_minor;
broadcom_trx_t* m__root;
broadcom_trx_t::tail_t::hw_comp_info_t* m__parent;
public:
uint8_t major() const { return m_major; }
uint8_t minor() const { return m_minor; }
broadcom_trx_t* _root() const { return m__root; }
broadcom_trx_t::tail_t::hw_comp_info_t* _parent() const { return m__parent; }
};
class version_t : public kaitai::kstruct {
public:
version_t(kaitai::kstream* p__io, broadcom_trx_t::tail_t* p__parent = 0, broadcom_trx_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~version_t();
private:
uint8_t m_major;
uint8_t m_minor;
uint8_t m_patch;
uint8_t m_tweak;
broadcom_trx_t* m__root;
broadcom_trx_t::tail_t* m__parent;
public:
uint8_t major() const { return m_major; }
uint8_t minor() const { return m_minor; }
uint8_t patch() const { return m_patch; }
uint8_t tweak() const { return m_tweak; }
broadcom_trx_t* _root() const { return m__root; }
broadcom_trx_t::tail_t* _parent() const { return m__parent; }
};
/**
* A safeguard against installation of firmware to an incompatible device
*/
class tail_t : public kaitai::kstruct {
public:
class hw_comp_info_t;
tail_t(kaitai::kstream* p__io, broadcom_trx_t* p__parent = 0, broadcom_trx_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~tail_t();
class hw_comp_info_t : public kaitai::kstruct {
public:
hw_comp_info_t(kaitai::kstream* p__io, broadcom_trx_t::tail_t* p__parent = 0, broadcom_trx_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~hw_comp_info_t();
private:
revision_t* m_min;
revision_t* m_max;
broadcom_trx_t* m__root;
broadcom_trx_t::tail_t* m__parent;
public:
revision_t* min() const { return m_min; }
revision_t* max() const { return m_max; }
broadcom_trx_t* _root() const { return m__root; }
broadcom_trx_t::tail_t* _parent() const { return m__parent; }
};
private:
version_t* m_version;
std::string m_product_id;
std::vector<hw_comp_info_t*>* m_comp_hw;
std::string m_reserved;
broadcom_trx_t* m__root;
broadcom_trx_t* m__parent;
public:
/**
* 1.9.2.7 by default
*/
version_t* version() const { return m_version; }
std::string product_id() const { return m_product_id; }
/**
* 0.02 - 2.99
*/
std::vector<hw_comp_info_t*>* comp_hw() const { return m_comp_hw; }
std::string reserved() const { return m_reserved; }
broadcom_trx_t* _root() const { return m__root; }
broadcom_trx_t* _parent() const { return m__parent; }
};
class header_t : public kaitai::kstruct {
public:
class partition_t;
class flags_t;
header_t(kaitai::kstream* p__io, broadcom_trx_t* p__parent = 0, broadcom_trx_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~header_t();
class partition_t : public kaitai::kstruct {
public:
partition_t(uint8_t p_idx, kaitai::kstream* p__io, broadcom_trx_t::header_t* p__parent = 0, broadcom_trx_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~partition_t();
private:
bool f_is_present;
bool m_is_present;
public:
bool is_present();
private:
bool f_is_last;
bool m_is_last;
bool n_is_last;
public:
bool _is_null_is_last() { is_last(); return n_is_last; };
private:
public:
bool is_last();
private:
bool f_len_body;
int32_t m_len_body;
bool n_len_body;
public:
bool _is_null_len_body() { len_body(); return n_len_body; };
private:
public:
int32_t len_body();
private:
bool f_body;
std::string m_body;
bool n_body;
public:
bool _is_null_body() { body(); return n_body; };
private:
public:
std::string body();
private:
uint32_t m_ofs_body;
uint8_t m_idx;
broadcom_trx_t* m__root;
broadcom_trx_t::header_t* m__parent;
public:
uint32_t ofs_body() const { return m_ofs_body; }
uint8_t idx() const { return m_idx; }
broadcom_trx_t* _root() const { return m__root; }
broadcom_trx_t::header_t* _parent() const { return m__parent; }
};
class flags_t : public kaitai::kstruct {
public:
flags_t(kaitai::kstream* p__io, broadcom_trx_t::header_t* p__parent = 0, broadcom_trx_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~flags_t();
private:
std::vector<bool>* m_flags;
broadcom_trx_t* m__root;
broadcom_trx_t::header_t* m__parent;
public:
std::vector<bool>* flags() const { return m_flags; }
broadcom_trx_t* _root() const { return m__root; }
broadcom_trx_t::header_t* _parent() const { return m__parent; }
};
private:
std::string m_magic;
uint32_t m_len;
uint32_t m_crc32;
uint16_t m_version;
flags_t* m_flags;
std::vector<partition_t*>* m_partitions;
broadcom_trx_t* m__root;
broadcom_trx_t* m__parent;
public:
std::string magic() const { return m_magic; }
/**
* Length of file including header
*/
uint32_t len() const { return m_len; }
/**
* CRC from `version` (??? todo: see the original and disambiguate) to end of file
*/
uint32_t crc32() const { return m_crc32; }
uint16_t version() const { return m_version; }
flags_t* flags() const { return m_flags; }
/**
* Offsets of partitions from start of header
*/
std::vector<partition_t*>* partitions() const { return m_partitions; }
broadcom_trx_t* _root() const { return m__root; }
broadcom_trx_t* _parent() const { return m__parent; }
};
private:
bool f_header;
header_t* m_header;
public:
header_t* header();
private:
bool f_tail;
tail_t* m_tail;
public:
tail_t* tail();
private:
broadcom_trx_t* m__root;
kaitai::kstruct* m__parent;
public:
broadcom_trx_t* _root() const { return m__root; }
kaitai::kstruct* _parent() const { return m__parent; }
};
#endif // BROADCOM_TRX_H_
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
#include "broadcom_trx.h"
#include "kaitai/exceptions.h"
broadcom_trx_t::broadcom_trx_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, broadcom_trx_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = this;
m_header = 0;
m_tail = 0;
f_header = false;
f_tail = false;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void broadcom_trx_t::_read() {
}
broadcom_trx_t::~broadcom_trx_t() {
_clean_up();
}
void broadcom_trx_t::_clean_up() {
if (f_header) {
if (m_header) {
delete m_header; m_header = 0;
}
}
if (f_tail) {
if (m_tail) {
delete m_tail; m_tail = 0;
}
}
}
broadcom_trx_t::revision_t::revision_t(kaitai::kstream* p__io, broadcom_trx_t::tail_t::hw_comp_info_t* p__parent, broadcom_trx_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void broadcom_trx_t::revision_t::_read() {
m_major = m__io->read_u1();
m_minor = m__io->read_u1();
}
broadcom_trx_t::revision_t::~revision_t() {
_clean_up();
}
void broadcom_trx_t::revision_t::_clean_up() {
}
broadcom_trx_t::version_t::version_t(kaitai::kstream* p__io, broadcom_trx_t::tail_t* p__parent, broadcom_trx_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void broadcom_trx_t::version_t::_read() {
m_major = m__io->read_u1();
m_minor = m__io->read_u1();
m_patch = m__io->read_u1();
m_tweak = m__io->read_u1();
}
broadcom_trx_t::version_t::~version_t() {
_clean_up();
}
void broadcom_trx_t::version_t::_clean_up() {
}
broadcom_trx_t::tail_t::tail_t(kaitai::kstream* p__io, broadcom_trx_t* p__parent, broadcom_trx_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_version = 0;
m_comp_hw = 0;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void broadcom_trx_t::tail_t::_read() {
m_version = new version_t(m__io, this, m__root);
m_product_id = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(m__io->read_bytes(12), 0, false), std::string("utf-8"));
m_comp_hw = new std::vector<hw_comp_info_t*>();
const int l_comp_hw = 4;
for (int i = 0; i < l_comp_hw; i++) {
m_comp_hw->push_back(new hw_comp_info_t(m__io, this, m__root));
}
m_reserved = m__io->read_bytes(32);
}
broadcom_trx_t::tail_t::~tail_t() {
_clean_up();
}
void broadcom_trx_t::tail_t::_clean_up() {
if (m_version) {
delete m_version; m_version = 0;
}
if (m_comp_hw) {
for (std::vector<hw_comp_info_t*>::iterator it = m_comp_hw->begin(); it != m_comp_hw->end(); ++it) {
delete *it;
}
delete m_comp_hw; m_comp_hw = 0;
}
}
broadcom_trx_t::tail_t::hw_comp_info_t::hw_comp_info_t(kaitai::kstream* p__io, broadcom_trx_t::tail_t* p__parent, broadcom_trx_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_min = 0;
m_max = 0;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void broadcom_trx_t::tail_t::hw_comp_info_t::_read() {
m_min = new revision_t(m__io, this, m__root);
m_max = new revision_t(m__io, this, m__root);
}
broadcom_trx_t::tail_t::hw_comp_info_t::~hw_comp_info_t() {
_clean_up();
}
void broadcom_trx_t::tail_t::hw_comp_info_t::_clean_up() {
if (m_min) {
delete m_min; m_min = 0;
}
if (m_max) {
delete m_max; m_max = 0;
}
}
broadcom_trx_t::header_t::header_t(kaitai::kstream* p__io, broadcom_trx_t* p__parent, broadcom_trx_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_flags = 0;
m_partitions = 0;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void broadcom_trx_t::header_t::_read() {
m_magic = m__io->read_bytes(4);
if (!(magic() == std::string("\x48\x44\x52\x30", 4))) {
throw kaitai::validation_not_equal_error<std::string>(std::string("\x48\x44\x52\x30", 4), magic(), _io(), std::string("/types/header/seq/0"));
}
m_len = m__io->read_u4le();
m_crc32 = m__io->read_u4le();
m_version = m__io->read_u2le();
m_flags = new flags_t(m__io, this, m__root);
m_partitions = new std::vector<partition_t*>();
{
int i = 0;
partition_t* _;
do {
_ = new partition_t(i, m__io, this, m__root);
m_partitions->push_back(_);
i++;
} while (!( ((i >= 4) || (!(_->is_present()))) ));
}
}
broadcom_trx_t::header_t::~header_t() {
_clean_up();
}
void broadcom_trx_t::header_t::_clean_up() {
if (m_flags) {
delete m_flags; m_flags = 0;
}
if (m_partitions) {
for (std::vector<partition_t*>::iterator it = m_partitions->begin(); it != m_partitions->end(); ++it) {
delete *it;
}
delete m_partitions; m_partitions = 0;
}
}
broadcom_trx_t::header_t::partition_t::partition_t(uint8_t p_idx, kaitai::kstream* p__io, broadcom_trx_t::header_t* p__parent, broadcom_trx_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_idx = p_idx;
f_is_present = false;
f_is_last = false;
f_len_body = false;
f_body = false;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void broadcom_trx_t::header_t::partition_t::_read() {
m_ofs_body = m__io->read_u4le();
}
broadcom_trx_t::header_t::partition_t::~partition_t() {
_clean_up();
}
void broadcom_trx_t::header_t::partition_t::_clean_up() {
if (f_body && !n_body) {
}
}
bool broadcom_trx_t::header_t::partition_t::is_present() {
if (f_is_present)
return m_is_present;
m_is_present = ofs_body() != 0;
f_is_present = true;
return m_is_present;
}
bool broadcom_trx_t::header_t::partition_t::is_last() {
if (f_is_last)
return m_is_last;
n_is_last = true;
if (is_present()) {
n_is_last = false;
m_is_last = ((idx() == (_parent()->partitions()->size() - 1)) || (!(_parent()->partitions()->at((idx() + 1))->is_present()))) ;
}
f_is_last = true;
return m_is_last;
}
int32_t broadcom_trx_t::header_t::partition_t::len_body() {
if (f_len_body)
return m_len_body;
n_len_body = true;
if (is_present()) {
n_len_body = false;
m_len_body = ((is_last()) ? ((_root()->_io()->size() - ofs_body())) : (_parent()->partitions()->at((idx() + 1))->ofs_body()));
}
f_len_body = true;
return m_len_body;
}
std::string broadcom_trx_t::header_t::partition_t::body() {
if (f_body)
return m_body;
n_body = true;
if (is_present()) {
n_body = false;
kaitai::kstream *io = _root()->_io();
std::streampos _pos = io->pos();
io->seek(ofs_body());
m_body = io->read_bytes(len_body());
io->seek(_pos);
f_body = true;
}
return m_body;
}
broadcom_trx_t::header_t::flags_t::flags_t(kaitai::kstream* p__io, broadcom_trx_t::header_t* p__parent, broadcom_trx_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_flags = 0;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void broadcom_trx_t::header_t::flags_t::_read() {
m_flags = new std::vector<bool>();
const int l_flags = 16;
for (int i = 0; i < l_flags; i++) {
m_flags->push_back(m__io->read_bits_int_le(1));
}
}
broadcom_trx_t::header_t::flags_t::~flags_t() {
_clean_up();
}
void broadcom_trx_t::header_t::flags_t::_clean_up() {
if (m_flags) {
delete m_flags; m_flags = 0;
}
}
broadcom_trx_t::header_t* broadcom_trx_t::header() {
if (f_header)
return m_header;
std::streampos _pos = m__io->pos();
m__io->seek(0);
m_header = new header_t(m__io, this, m__root);
m__io->seek(_pos);
f_header = true;
return m_header;
}
broadcom_trx_t::tail_t* broadcom_trx_t::tail() {
if (f_tail)
return m_tail;
std::streampos _pos = m__io->pos();
m__io->seek((_io()->size() - 64));
m_tail = new tail_t(m__io, this, m__root);
m__io->seek(_pos);
f_tail = true;
return m_tail;
}