// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild using System.Collections.Generic; namespace Kaitai { /// /// MiniFS is a file system found in certain firmware files from TP-Link /// (including its Mercusys brand) and Mercury. Such firmware contains an RTOS /// kernel, e.g. VxWorks in /// [`RE450(V4)_210531.zip`](https://static.tp-link.com/firmware/2021/202107/20210709/RE450(V4)_210531.zip) /// ([archived](https://web.archive.org/web/20261105175157/https://static.tp-link.com/firmware/2021/202107/20210709/RE450(V4)_210531.zip)) /// or TPOS, TP-Link's RTOS, in /// [`TL-WA850RE(EU)_V7.0_250808.zip`](https://static.tp-link.com/upload/firmware/2025/202509/20250919/TL-WA850RE(EU)_V7.0_250808.zip) /// ([archived](https://web.archive.org/web/20260903180047/https://static.tp-link.com/upload/firmware/2025/202509/20250919/TL-WA850RE(EU)_V7.0_250808.zip)). /// /// According to a TP-Link manager quoted in a /// [2021 article](https://shiftdelete.net/tp-link-mercusys-ile-ilgili-hedeflerini-paylasti) /// ([archived](https://web.archive.org/web/20230513175833/https://shiftdelete.net/tp-link-mercusys-ile-ilgili-hedeflerini-paylasti)), /// Mercusys has been sold in China as Mercury since 2001. All Mercury images /// examined contain a certificate with `O=tplink` in its issuer and subject. /// /// All versions share the same header, and its `version` field determines the /// layout of the rest of the image. /// /// In version 3, the header is followed by a table of directory paths and file /// names, followed by meta information for each file, meta information about /// the LZMA compressed blobs including length, offset and uncompressed length, /// and then a number of LZMA compressed blobs. /// /// The LZMA blobs can contain the concatenated contents of multiple files. /// The meta information of a file contains the offsets of its directory path /// and file name, the LZMA blob (numbering starts at 0), the offset of the file /// in the uncompressed data and the size of the uncompressed file. /// /// Some version 3 images have no uncompressed length in the meta information /// about the blobs (see `has_8_byte_lzma_metas` in `body_v3`). /// /// In version 2, the header is followed by the length of the files area and a /// meta information record for each file, each followed directly by the file's /// data. Unlike versions 1 and 3, version 2 stores no paths or file names at /// all, only the MD5 hash of each file's path. /// /// In version 1, the header is followed by a record with the path, the compressed /// length and the offset of each file, and then one LZMA blob per file. /// /// This Kaitai Struct implementation was written independently of the paper /// [Reverse Engineered MiniFS File System](https://arxiv.org/abs/2407.05064v1). /// It was started in the firmware analysis project /// [BANG](https://github.com/armijnhemel/binaryanalysis-ng) (Binary Analysis /// Next Generation) in /// [2021](https://github.com/armijnhemel/binaryanalysis-ng/commit/531203a076e3761116320b77b2b9c64a9dca6186) /// and submitted to the Kaitai Struct format gallery in /// [September 2023](https://github.com/kaitai-io/kaitai_struct_formats/pull/682), /// whereas the paper was published in July 2024. /// /// /// Reference: The paper, section 4 (version 3) /// /// /// Reference: tp_research's `Minifs.py` (version 2) /// /// /// Reference: unblob's MiniFS handler (version 3) /// public partial class TplinkMinifs : KaitaiStruct { public static TplinkMinifs FromFile(string fileName) { return new TplinkMinifs(new KaitaiStream(fileName)); } public enum CompressionType { Lzma = 0, Raw = 2, Lzma2014 = 4, } public TplinkMinifs(KaitaiStream p__io, KaitaiStruct p__parent = null, TplinkMinifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root ?? this; _read(); } private void _read() { _header = new MinifsHeader(m_io, this, m_root); switch (Header.Version) { case 1: { _body = new BodyV1(m_io, this, m_root); break; } case 2: { _body = new BodyV2(m_io, this, m_root); break; } case 3: { _body = new BodyV3(m_io, this, m_root); break; } } } /// /// Reference: Source /// public partial class BodyV1 : KaitaiStruct { public static BodyV1 FromFile(string fileName) { return new BodyV1(new KaitaiStream(fileName)); } public BodyV1(KaitaiStream p__io, TplinkMinifs p__parent = null, TplinkMinifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { _inodes = new List(); for (var i = 0; i < M_Root.Header.NumFiles; i++) { _inodes.Add(new InodeV1(((uint) ((i != 0 ? Inodes[i - 1].OfsEnd : 0))), m_io, this, m_root)); } __raw_blobsArea = m_io.ReadBytes(Inodes[Inodes.Count - 1].OfsEnd); var io___raw_blobsArea = new KaitaiStream(__raw_blobsArea); _blobsArea = new Dummy(io___raw_blobsArea, this, m_root); } private List _inodes; private Dummy _blobsArea; private TplinkMinifs m_root; private TplinkMinifs m_parent; private byte[] __raw_blobsArea; public List Inodes { get { return _inodes; } } public Dummy BlobsArea { get { return _blobsArea; } } public TplinkMinifs M_Root { get { return m_root; } } public TplinkMinifs M_Parent { get { return m_parent; } } public byte[] M_RawBlobsArea { get { return __raw_blobsArea; } } } public partial class BodyV2 : KaitaiStruct { public static BodyV2 FromFile(string fileName) { return new BodyV2(new KaitaiStream(fileName)); } public BodyV2(KaitaiStream p__io, TplinkMinifs p__parent = null, TplinkMinifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { _lenFilesArea = m_io.ReadU4be(); __raw_filesArea = m_io.ReadBytes(LenFilesArea); var io___raw_filesArea = new KaitaiStream(__raw_filesArea); _filesArea = new FilesAreaV2(io___raw_filesArea, this, m_root); } private uint _lenFilesArea; private FilesAreaV2 _filesArea; private TplinkMinifs m_root; private TplinkMinifs m_parent; private byte[] __raw_filesArea; public uint LenFilesArea { get { return _lenFilesArea; } } public FilesAreaV2 FilesArea { get { return _filesArea; } } public TplinkMinifs M_Root { get { return m_root; } } public TplinkMinifs M_Parent { get { return m_parent; } } public byte[] M_RawFilesArea { get { return __raw_filesArea; } } } public partial class BodyV3 : KaitaiStruct { public static BodyV3 FromFile(string fileName) { return new BodyV3(new KaitaiStream(fileName)); } public BodyV3(KaitaiStream p__io, TplinkMinifs p__parent = null, TplinkMinifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; f_has8ByteLzmaMetas = false; f_lenFirstBlobUncompressedIf8ByteMetas = false; f_lenLzmaMeta = false; f_numLzmaBlobs = false; f_ofsLzmaMetas = false; _read(); } private void _read() { _lenNameTable = m_io.ReadU4be(); __raw_nameTable = m_io.ReadBytes(LenNameTable); var io___raw_nameTable = new KaitaiStream(__raw_nameTable); _nameTable = new NameTable(io___raw_nameTable, this, m_root); _inodes = new List(); for (var i = 0; i < M_Root.Header.NumFiles; i++) { _inodes.Add(new Inode(((uint) ((i != 0 ? Inodes[i - 1].CurMaxLzmaBlob : 0))), m_io, this, m_root)); } if (OfsLzmaMetas < 0) { __unnamed3 = m_io.ReadBytes(0); } _lzmaMetas = new List(); for (var i = 0; i < NumLzmaBlobs; i++) { _lzmaMetas.Add(new LzmaMeta(((uint) ((i != 0 ? LzmaMetas[i - 1].OfsEnd : 0))), m_io, this, m_root)); } __raw_lzmaBlobsArea = m_io.ReadBytes(LzmaMetas[LzmaMetas.Count - 1].OfsEnd); var io___raw_lzmaBlobsArea = new KaitaiStream(__raw_lzmaBlobsArea); _lzmaBlobsArea = new Dummy(io___raw_lzmaBlobsArea, this, m_root); } private bool f_has8ByteLzmaMetas; private bool _has8ByteLzmaMetas; /// /// Whether each record in `lzma_metas` lacks `len_blob_uncompressed` and /// is therefore 8 bytes in size instead of 12. /// /// The MiniFS header doesn't say which size is used (both use version 3), /// so this Kaitai Struct implementation infers it using a heuristic based /// on the `.lzma` header of the first blob. Bytes 5 to 12 of that header /// are the uncompressed size, which was unknown (all bits set) for all /// blobs in all version 3 images examined. So with 8-byte records, /// `len_first_blob_uncompressed_if_8_byte_metas` has all bits set. With /// 12-byte records, the same 8 bytes aren't the uncompressed size. With 3 /// or more blobs, they include a whole offset or length in `lzma_metas`, /// which would have to be 4 GiB. With 1 or 2 blobs, they include the /// first blob's dictionary size, which is 8 MiB in all images examined. /// /// An image with 8-byte records and a known uncompressed size would be /// rejected, or misparsed if it has a single blob and other data follows it. /// /// In the images examined, 8-byte records occur in firmware files with /// modification times from 2018 to 2022 (as stored in their ZIPs), e.g. in /// [`MW301R(EU)_V2_180821.zip`](https://static.mercusys.com/software/MW301R(EU)_V2_180821.zip) /// ([archived](https://web.archive.org/web/20261105093950/https://static.mercusys.com/software/MW301R(EU)_V2_180821.zip)), /// listed on <https://www.mercusys.com/en/download/mw301r/v2/> /// ([archived](https://web.archive.org/web/20261105142659/https://www.mercusys.com/en/download/mw301r/v2/)). /// /// As far as we know, this implementation is the first description of /// 8-byte records. The other descriptions and tools found either assume /// 12 bytes or don't read `lzma_metas` at all. So there is no reference /// to compare it with, and the evidence comes from the images. In all 49 /// distinct images examined with 8-byte records, the bytes at each /// record's offset and length are exactly one complete LZMA stream. Each /// such stream decompresses to exactly the length that the files packed /// into it need. The file that ends last in it ends exactly where the /// decompressed data ends. In addition: /// /// * 2614 of the 7230 files in these 49 images have the same path and /// contents as a file in an image with 12-byte records. For example, /// 49 files are the same in the image with 8-byte records in /// [`TL-WA850RE(EU)_V7_201029.zip`](https://static.tp-link.com/2021/202102/20210209/TL-WA850RE(EU)_V7_201029.zip) /// ([archived](https://web.archive.org/web/20220326032207/https://static.tp-link.com/2021/202102/20210209/TL-WA850RE(EU)_V7_201029.zip)) /// and the image with 12-byte records in /// [`TL-WA850RE(EU)_V7.0_250808.zip`](https://static.tp-link.com/upload/firmware/2025/202509/20250919/TL-WA850RE(EU)_V7.0_250808.zip) /// ([archived](https://web.archive.org/web/20260903180047/https://static.tp-link.com/upload/firmware/2025/202509/20250919/TL-WA850RE(EU)_V7.0_250808.zip)). /// * The repository /// [NurMohammadDuari/mw325r-hack-lab](https://github.com/NurMohammadDuari/mw325r-hack-lab/tree/024da680dbf4f9ca287d562622f02d7d8875bd45) /// has the [files](https://github.com/NurMohammadDuari/mw325r-hack-lab/tree/024da680dbf4f9ca287d562622f02d7d8875bd45/extracted-minifs) /// of the image with 8-byte records in /// [`MW325R(EU)_V2_20111620201211065426.zip`](https://static.mercusys.com/software/MW325R(EU)_V2_20111620201211065426.zip) /// ([archived](https://web.archive.org/web/20261105093427/https://static.mercusys.com/software/MW325R(EU)_V2_20111620201211065426.zip)). /// They match the files parsed by this implementation in all 92 files /// that can be compared. Its /// [extractor](https://github.com/NurMohammadDuari/mw325r-hack-lab/blob/024da680dbf4f9ca287d562622f02d7d8875bd45/analysis/minifs_extract.py) /// finds the blobs by searching for LZMA headers instead of reading /// `lzma_metas`. /// public bool Has8ByteLzmaMetas { get { if (f_has8ByteLzmaMetas) return _has8ByteLzmaMetas; f_has8ByteLzmaMetas = true; _has8ByteLzmaMetas = (bool) (LenFirstBlobUncompressedIf8ByteMetas == 18446744073709551615); return _has8ByteLzmaMetas; } } private bool f_lenFirstBlobUncompressedIf8ByteMetas; private ulong _lenFirstBlobUncompressedIf8ByteMetas; /// /// The uncompressed size in the `.lzma` header of the first blob, if /// `lzma_metas` have 8-byte records. It follows the properties byte and the /// 4-byte dictionary size. /// public ulong LenFirstBlobUncompressedIf8ByteMetas { get { if (f_lenFirstBlobUncompressedIf8ByteMetas) return _lenFirstBlobUncompressedIf8ByteMetas; f_lenFirstBlobUncompressedIf8ByteMetas = true; long _pos = m_io.Pos; m_io.Seek((OfsLzmaMetas + 8 * NumLzmaBlobs) + 5); _lenFirstBlobUncompressedIf8ByteMetas = m_io.ReadU8le(); m_io.Seek(_pos); return _lenFirstBlobUncompressedIf8ByteMetas; } } private bool f_lenLzmaMeta; private sbyte _lenLzmaMeta; /// /// The size of each record in `lzma_metas` (8 or 12 bytes). /// public sbyte LenLzmaMeta { get { if (f_lenLzmaMeta) return _lenLzmaMeta; f_lenLzmaMeta = true; _lenLzmaMeta = (sbyte) ((Has8ByteLzmaMetas ? 8 : 12)); return _lenLzmaMeta; } } private bool f_numLzmaBlobs; private int _numLzmaBlobs; /// /// Count starts at 0, so there is one more blob than the highest blob number /// /// /// Reference: Source /// public int NumLzmaBlobs { get { if (f_numLzmaBlobs) return _numLzmaBlobs; f_numLzmaBlobs = true; _numLzmaBlobs = (int) (Inodes[Inodes.Count - 1].CurMaxLzmaBlob + 1); return _numLzmaBlobs; } } private bool f_ofsLzmaMetas; private int _ofsLzmaMetas; public int OfsLzmaMetas { get { if (f_ofsLzmaMetas) return _ofsLzmaMetas; f_ofsLzmaMetas = true; _ofsLzmaMetas = (int) (M_Io.Pos); return _ofsLzmaMetas; } } private uint _lenNameTable; private NameTable _nameTable; private List _inodes; private byte[] __unnamed3; private List _lzmaMetas; private Dummy _lzmaBlobsArea; private TplinkMinifs m_root; private TplinkMinifs m_parent; private byte[] __raw_nameTable; private byte[] __raw_lzmaBlobsArea; public uint LenNameTable { get { return _lenNameTable; } } public NameTable NameTable { get { return _nameTable; } } public List Inodes { get { return _inodes; } } public byte[] Unnamed_3 { get { return __unnamed3; } } public List LzmaMetas { get { return _lzmaMetas; } } public Dummy LzmaBlobsArea { get { return _lzmaBlobsArea; } } public TplinkMinifs M_Root { get { return m_root; } } public TplinkMinifs M_Parent { get { return m_parent; } } public byte[] M_RawNameTable { get { return __raw_nameTable; } } public byte[] M_RawLzmaBlobsArea { get { return __raw_lzmaBlobsArea; } } } public partial class Dummy : KaitaiStruct { public static Dummy FromFile(string fileName) { return new Dummy(new KaitaiStream(fileName)); } public Dummy(KaitaiStream p__io, KaitaiStruct p__parent = null, TplinkMinifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { } private TplinkMinifs m_root; private KaitaiStruct m_parent; public TplinkMinifs M_Root { get { return m_root; } } public KaitaiStruct M_Parent { get { return m_parent; } } } /// /// Reference: Source /// public partial class FileV2 : KaitaiStruct { public static FileV2 FromFile(string fileName) { return new FileV2(new KaitaiStream(fileName)); } public FileV2(KaitaiStream p__io, TplinkMinifs.FilesAreaV2 p__parent = null, TplinkMinifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { _pathMd5 = m_io.ReadBytes(16); _lenData = m_io.ReadU4be(); _ofsData = m_io.ReadU4be(); if (!(_ofsData == M_Io.Pos + 8)) { throw new ValidationNotEqualError(M_Io.Pos + 8, _ofsData, m_io, "/types/file_v2/seq/2"); } _lenDataUncompressed = m_io.ReadU4be(); _compression = ((TplinkMinifs.CompressionType) m_io.ReadU4be()); _data = m_io.ReadBytes(LenData); } private byte[] _pathMd5; private uint _lenData; private uint _ofsData; private uint _lenDataUncompressed; private CompressionType _compression; private byte[] _data; private TplinkMinifs m_root; private TplinkMinifs.FilesAreaV2 m_parent; /// /// The MD5 hash of the absolute path of the file, e.g. of /// `/conf/priv-key.pem`. /// /// Version 2 images store only these hashes, not the paths, so a path can /// only be found by hashing candidate paths and comparing. /// [`Minifs.py`](https://github.com/zh-explorer/tp_research/blob/a42f9d0427ad0c453a7060f3236c4174bb883441/Minifs.py#L181-L209) /// takes the candidates from path-like strings in the files' contents. /// public byte[] PathMd5 { get { return _pathMd5; } } public uint LenData { get { return _lenData; } } /// /// Offset of `data` relative to the start of `<body_v2>.files_area`. /// public uint OfsData { get { return _ofsData; } } public uint LenDataUncompressed { get { return _lenDataUncompressed; } } public CompressionType Compression { get { return _compression; } } /// /// With `compression_type::lzma`, an LZMA stream in the `.lzma` format /// (also known as LZMA_Alone). Decompress it in the same way as /// `<lzma_meta>.blob`. /// /// In all images examined, the `.lzma` header specifies the properties /// lc = 0, lp = 0, pb = 2 and an 8 MiB dictionary, so it starts with the /// bytes `5A 00 00 80 00`. It also specifies the uncompressed size of the /// stream, and there is no end-of-payload marker. /// public byte[] Data { get { return _data; } } public TplinkMinifs M_Root { get { return m_root; } } public TplinkMinifs.FilesAreaV2 M_Parent { get { return m_parent; } } } public partial class FilesAreaV2 : KaitaiStruct { public static FilesAreaV2 FromFile(string fileName) { return new FilesAreaV2(new KaitaiStream(fileName)); } public FilesAreaV2(KaitaiStream p__io, TplinkMinifs.BodyV2 p__parent = null, TplinkMinifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { _files = new List(); for (var i = 0; i < M_Root.Header.NumFiles; i++) { _files.Add(new FileV2(m_io, this, m_root)); } } private List _files; private TplinkMinifs m_root; private TplinkMinifs.BodyV2 m_parent; public List Files { get { return _files; } } public TplinkMinifs M_Root { get { return m_root; } } public TplinkMinifs.BodyV2 M_Parent { get { return m_parent; } } } /// /// Reference: Source /// /// /// Reference: Source /// public partial class Inode : KaitaiStruct { public Inode(uint p_prevMaxLzmaBlob, KaitaiStream p__io, TplinkMinifs.BodyV3 p__parent = null, TplinkMinifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _prevMaxLzmaBlob = p_prevMaxLzmaBlob; f_curMaxLzmaBlob = false; f_directoryPath = false; f_fileName = false; _read(); } private void _read() { _ofsDirectoryPath = m_io.ReadU4be(); _ofsFileName = m_io.ReadU4be(); _lzmaBlob = m_io.ReadU4be(); _ofsFile = m_io.ReadU4be(); _lenFile = m_io.ReadU4be(); } private bool f_curMaxLzmaBlob; private int _curMaxLzmaBlob; public int CurMaxLzmaBlob { get { if (f_curMaxLzmaBlob) return _curMaxLzmaBlob; f_curMaxLzmaBlob = true; _curMaxLzmaBlob = (int) ((LzmaBlob > PrevMaxLzmaBlob ? LzmaBlob : PrevMaxLzmaBlob)); return _curMaxLzmaBlob; } } private bool f_directoryPath; private string _directoryPath; public string DirectoryPath { get { if (f_directoryPath) return _directoryPath; f_directoryPath = true; KaitaiStream io = M_Parent.NameTable.M_Io; long _pos = io.Pos; io.Seek(OfsDirectoryPath); _directoryPath = System.Text.Encoding.GetEncoding("ASCII").GetString(io.ReadBytesTerm(0, false, true, true)); io.Seek(_pos); return _directoryPath; } } private bool f_fileName; private string _fileName; public string FileName { get { if (f_fileName) return _fileName; f_fileName = true; KaitaiStream io = M_Parent.NameTable.M_Io; long _pos = io.Pos; io.Seek(OfsFileName); _fileName = System.Text.Encoding.GetEncoding("ASCII").GetString(io.ReadBytesTerm(0, false, true, true)); io.Seek(_pos); return _fileName; } } private uint _ofsDirectoryPath; private uint _ofsFileName; private uint _lzmaBlob; private uint _ofsFile; private uint _lenFile; private uint _prevMaxLzmaBlob; private TplinkMinifs m_root; private TplinkMinifs.BodyV3 m_parent; public uint OfsDirectoryPath { get { return _ofsDirectoryPath; } } public uint OfsFileName { get { return _ofsFileName; } } public uint LzmaBlob { get { return _lzmaBlob; } } public uint OfsFile { get { return _ofsFile; } } public uint LenFile { get { return _lenFile; } } public uint PrevMaxLzmaBlob { get { return _prevMaxLzmaBlob; } } public TplinkMinifs M_Root { get { return m_root; } } public TplinkMinifs.BodyV3 M_Parent { get { return m_parent; } } } public partial class InodeV1 : KaitaiStruct { public InodeV1(uint p_prevOfsEnd, KaitaiStream p__io, TplinkMinifs.BodyV1 p__parent = null, TplinkMinifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _prevOfsEnd = p_prevOfsEnd; f_blob = false; f_ofsEnd = false; _read(); } private void _read() { _path = System.Text.Encoding.GetEncoding("ASCII").GetString(KaitaiStream.BytesTerminate(m_io.ReadBytes(80), 0, false)); _lenBlob = m_io.ReadU4be(); _ofsBlob = m_io.ReadU4be(); if (!(_ofsBlob == PrevOfsEnd)) { throw new ValidationNotEqualError(PrevOfsEnd, _ofsBlob, m_io, "/types/inode_v1/seq/2"); } } private bool f_blob; private byte[] _blob; /// /// An LZMA stream in the `.lzma` format (also known as LZMA_Alone) with /// the contents of the file. Decompress it in the same way as /// `<lzma_meta>.blob`. /// /// In all images examined, the `.lzma` header specifies the properties /// lc = 0, lp = 0, pb = 2 and an 8 MiB dictionary, so it starts with the /// bytes `5A 00 00 80 00`. It also specifies the uncompressed size of the /// stream, and there is no end-of-payload marker. /// public byte[] Blob { get { if (f_blob) return _blob; f_blob = true; KaitaiStream io = M_Parent.BlobsArea.M_Io; long _pos = io.Pos; io.Seek(OfsBlob); _blob = io.ReadBytes(LenBlob); io.Seek(_pos); return _blob; } } private bool f_ofsEnd; private int _ofsEnd; public int OfsEnd { get { if (f_ofsEnd) return _ofsEnd; f_ofsEnd = true; _ofsEnd = (int) (OfsBlob + LenBlob); return _ofsEnd; } } private string _path; private uint _lenBlob; private uint _ofsBlob; private uint _prevOfsEnd; private TplinkMinifs m_root; private TplinkMinifs.BodyV1 m_parent; public string Path { get { return _path; } } public uint LenBlob { get { return _lenBlob; } } /// /// Relative to the start of `<body_v1>.blobs_area`. /// public uint OfsBlob { get { return _ofsBlob; } } public uint PrevOfsEnd { get { return _prevOfsEnd; } } public TplinkMinifs M_Root { get { return m_root; } } public TplinkMinifs.BodyV1 M_Parent { get { return m_parent; } } } /// /// Reference: Source /// /// /// Reference: Source /// public partial class LzmaMeta : KaitaiStruct { public LzmaMeta(uint p_prevOfsEnd, KaitaiStream p__io, TplinkMinifs.BodyV3 p__parent = null, TplinkMinifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _prevOfsEnd = p_prevOfsEnd; f_blob = false; f_ofsEnd = false; _read(); } private void _read() { _ofsBlob = m_io.ReadU4be(); if (!(_ofsBlob == PrevOfsEnd)) { throw new ValidationNotEqualError(PrevOfsEnd, _ofsBlob, m_io, "/types/lzma_meta/seq/0"); } _lenBlob = m_io.ReadU4be(); if (!(M_Parent.Has8ByteLzmaMetas)) { _lenBlobUncompressed = m_io.ReadU4be(); } } private bool f_blob; private byte[] _blob; /// /// An LZMA stream in the `.lzma` format (also known as LZMA_Alone): /// a 13-byte header followed by the compressed data. Decompress it /// with a ready-made LZMA decoder that reads this header, e.g. in /// Python with `lzma.decompress(blob, format=lzma.FORMAT_ALONE)`. The /// size of the decompressed data is `len_blob_uncompressed`, if present. /// /// In all images examined with `header.version` = 3, the `.lzma` header /// specifies the properties lc = 3, lp = 0, pb = 2 and an 8 MiB /// dictionary, so it starts with the bytes `5D 00 00 80 00`. The /// uncompressed size in the header is unknown (`0xFFFF_FFFF_FFFF_FFFF`), /// so the stream ends with an end-of-payload marker. /// /// /// Reference: Source /// /// /// Reference: Source /// public byte[] Blob { get { if (f_blob) return _blob; f_blob = true; KaitaiStream io = M_Parent.LzmaBlobsArea.M_Io; long _pos = io.Pos; io.Seek(OfsBlob); _blob = io.ReadBytes(LenBlob); io.Seek(_pos); return _blob; } } private bool f_ofsEnd; private int _ofsEnd; public int OfsEnd { get { if (f_ofsEnd) return _ofsEnd; f_ofsEnd = true; _ofsEnd = (int) (OfsBlob + LenBlob); return _ofsEnd; } } private uint _ofsBlob; private uint _lenBlob; private uint? _lenBlobUncompressed; private uint _prevOfsEnd; private TplinkMinifs m_root; private TplinkMinifs.BodyV3 m_parent; public uint OfsBlob { get { return _ofsBlob; } } public uint LenBlob { get { return _lenBlob; } } public uint? LenBlobUncompressed { get { return _lenBlobUncompressed; } } public uint PrevOfsEnd { get { return _prevOfsEnd; } } public TplinkMinifs M_Root { get { return m_root; } } public TplinkMinifs.BodyV3 M_Parent { get { return m_parent; } } } /// /// Reference: Source /// /// /// Reference: Source /// public partial class MinifsHeader : KaitaiStruct { public static MinifsHeader FromFile(string fileName) { return new MinifsHeader(new KaitaiStream(fileName)); } public MinifsHeader(KaitaiStream p__io, TplinkMinifs p__parent = null, TplinkMinifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { _magic = m_io.ReadBytes(16); if (!((KaitaiStream.ByteArrayCompare(_magic, new byte[] { 77, 73, 78, 73, 70, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }) == 0))) { throw new ValidationNotEqualError(new byte[] { 77, 73, 78, 73, 70, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, _magic, m_io, "/types/minifs_header/seq/0"); } _version = m_io.ReadU4be(); if (!( ((_version == 1) || (_version == 2) || (_version == 3)) )) { throw new ValidationNotAnyOfError(_version, m_io, "/types/minifs_header/seq/1"); } _numFiles = m_io.ReadU4be(); _lenLargestFile = m_io.ReadU4be(); } private byte[] _magic; private uint _version; private uint _numFiles; private uint _lenLargestFile; private TplinkMinifs m_root; private TplinkMinifs m_parent; public byte[] Magic { get { return _magic; } } public uint Version { get { return _version; } } public uint NumFiles { get { return _numFiles; } } /// /// The uncompressed size of the largest file. In all images examined with /// version 3, the inodes are sorted by size in descending order, so it is /// also the size of the first file. In all images examined with versions /// 1 and 2, the value is rounded up to a multiple of 4. /// public uint LenLargestFile { get { return _lenLargestFile; } } public TplinkMinifs M_Root { get { return m_root; } } public TplinkMinifs M_Parent { get { return m_parent; } } } /// /// Table of null-terminated directory paths and file names, referenced by /// `<inode>.ofs_directory_path` and `<inode>.ofs_file_name`. In all images /// examined, each non-empty string occurs only once, which saves space in /// flash memory, as many files share a directory or a name. /// /// In all images examined, the table is padded with null bytes to a multiple /// of 4 bytes, so `names` can end with up to 3 empty strings (one for each /// padding byte). /// /// /// Reference: Source /// public partial class NameTable : KaitaiStruct { public static NameTable FromFile(string fileName) { return new NameTable(new KaitaiStream(fileName)); } public NameTable(KaitaiStream p__io, TplinkMinifs.BodyV3 p__parent = null, TplinkMinifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { _names = new List(); { var i = 0; while (!m_io.IsEof) { _names.Add(System.Text.Encoding.GetEncoding("ASCII").GetString(m_io.ReadBytesTerm(0, false, true, true))); i++; } } } private List _names; private TplinkMinifs m_root; private TplinkMinifs.BodyV3 m_parent; public List Names { get { return _names; } } public TplinkMinifs M_Root { get { return m_root; } } public TplinkMinifs.BodyV3 M_Parent { get { return m_parent; } } } private MinifsHeader _header; private KaitaiStruct _body; private TplinkMinifs m_root; private KaitaiStruct m_parent; public MinifsHeader Header { get { return _header; } } public KaitaiStruct Body { get { return _body; } } public TplinkMinifs M_Root { get { return m_root; } } public KaitaiStruct M_Parent { get { return m_parent; } } } }