---
title: "Cabinet reader"
url: https://recursiveai.org/onearchive/docs/cab-reader/
description: "Normative notes for CabReader in RecursiveAI.OneArchive.Cab: read only. Source: Microsoft's [MS-CAB] (Cabinet File Format) 2.1 to 2.4 and 3.1; LZX is in…"
version: "0.7.0-preview.1"
source: https://github.com/recursive-ai-oss/onearchive-csharp/blob/3fa11f22dbe4e13c90ae9c63f393e945a0c09bba/docs/cab-reader.md
---

# Cabinet reader

Normative notes for `CabReader` in `RecursiveAI.OneArchive.Cab`: read only. Source: Microsoft's [MS-CAB] (Cabinet File Format) 2.1 to
2.4 and 3.1; LZX is in `lzx-decoder.md`. Gaps were closed with cabextract 1.11 and gcab 1.6 run as black boxes; findings are marked
**[tool]**.

## Layout read

All integers are little-endian.

- **CFHEADER** (36 bytes): `MSCF`, reserved, `cbCabinet`, reserved, `coffFiles`, reserved, version minor and major, `cFolders`, `cFiles`,
  `flags`, `setID`, `iCabinet`. Then, if the reserve flag (4) is set, `cbCFHeader` (u16, at most 60000), `cbCFFolder` (u8), `cbCFData` (u8) and
  `cbCFHeader` reserved bytes; then the NUL-terminated `szCabinetPrev`, `szDiskPrev` (flag 1) and `szCabinetNext`, `szDiskNext` (flag 2), each
  at most 255 bytes. Reserved fields are not checked; major version must be 1 (minor is exposed, not checked); unknown flag bits are ignored.
  `coffFiles` must lie after the folder table and inside the file.
- **CFFOLDER** (8 bytes + `cbCFFolder`): `coffCabStart`, `cCFData`, `typeCompress`. Method = `typeCompress & 15` (0 none, 1 MSZIP, 2
  Quantum, 3 LZX); LZX window = `typeCompress >> 8 & 31`, which must be 15 to 21 (other values raise `ArchiveUnsupportedException`).
- **CFFILE** (16 bytes + name): `cbFile`, `uoffFolderStart`, `iFolder`, DOS date, DOS time, `attribs`, NUL-terminated name of at most 256
  bytes. `iFolder` 0xFFFD, 0xFFFE and 0xFFFF mark files continued from the previous cabinet, into the next one, or both; they are listed
  (`CabEntry.Continuation`) but cannot be opened (`ArchiveUnsupportedException` naming the neighbouring cabinet). A file that is not continued
  must satisfy `uoffFolderStart + cbFile <= min(0x7FFF8000, cCFData * 32768)`.
- **CFDATA**: `csum` (u32), `cbData` (u16), `cbUncomp` (u16), `cbCFData` reserved bytes, `cbData` bytes. Blocks of a folder are contiguous
  from `coffCabStart`. `cbUncomp` 0 means the block continues in the next cabinet (`ArchiveUnsupportedException`); more than 32768 is an error.

## Names, times, attributes

- A name with `_A_NAME_IS_UTF` (0x80) is UTF-8 (invalid sequences become U+FFFD); otherwise it is ISO 8859-1 unless
  `CabReaderOptions.LegacyNameEncoding` says otherwise. `\` becomes `/` in `ArchiveEntry.Name`; the stored bytes are in `RawName`.
- DOS date and time are read as UTC, like zip's. A date or time that is not valid gives no `ModifiedTime`.
- `Mode` is 0444, plus owner write unless `_A_RDONLY`, plus execute for everyone with `_A_EXEC`. Hidden and system are only in `Attributes`.

## Data blocks

- **Checksum** ([MS-CAB] 3.1): XOR of the little-endian 32-bit words of the data, then the same function continued over `cbData` and
  `cbUncomp` (four bytes), where a trailing partial word is built from the last byte down. **[tool]** The data reserve is not included,
  although the specification calls the second part the "header area": with reserve bytes present, only this reading extracts under
  cabextract. A zero `csum` is not checked. A mismatch raises `CompressionFormatException` at the block's offset unless
  `VerifyChecksum` is false.
- **Stored**: `cbData` must equal `cbUncomp`.
- **MSZIP**: the block is `CK` then a raw deflate stream that ends with a final block and produces exactly `cbUncomp` bytes. Matches may
  reach into the previous 32 KiB of the folder's output (which is the history of the folder, not of the block); the reader keeps that
  history across blocks and uses the deflate decoder's preset dictionary for it. **[tool]** Reference tools assume every block but the last
  has 32768 bytes; this reader accepts any size.
- **LZX**: one frame per block with `cbUncomp` 32768 (the last block may be shorter; any other short block is an error); a cabinet is
  decoded with one `LzxDecoder` per folder.

## Access and limits

- The source must be seekable (`Archive` copies a forward-only source into memory first, subject to `MaxTotalSize`).
- A folder is decoded from its first block. Reading files in table order costs one pass; going back decodes from the start again. At
  most two idle decoders are kept. Entries are listed in table order.
- Limits: `MaxEntryCount`, `MaxPathLength`, `MaxPathDepth`, `MaxTotalSize` (declared sizes, checked while listing),
  `MaxExpansionRatio` (compressed bytes counted per block including headers, output per block), `MaxWindowSize` (the LZX window,
  checked when an entry is opened). Errors name the format `cab` and the offset of the field or block at fault; errors from the
  deflate and LZX decoders are re-raised with the cabinet offset of the block and their own description.
- Not supported: Quantum folders (`ArchiveUnsupportedException`), files and blocks that span cabinets, LZX windows beyond 2^21, writing.
