pcf-debug 0.0.2

Inspection and visualisation tool for Partitioned Container Format (PCF) files
Documentation

pcf-debug

A read-only inspector and visualiser for Partitioned Container Format (PCF) files. It walks a file's physical structure, renders the byte layout and partition table as text or as a self-contained HTML report, and decodes partition contents into field trees through a small plugin system.

It is built on the reference pcf crate and reuses its byte parsers, so it never re-implements the format — but unlike pcf::Container, it walks the table-block chain directly and tolerates corrupt files, surfacing anomalies as diagnostics instead of erroring out.

Build & run

From the repository root (a Cargo workspace ties the tool to the reference crate):

# build
cargo build -p pcf-debug

# produce a sample file to look at
cargo run -p pcf --example gen_testvector -- /tmp/tv.pcf

# inspect it
cargo run -p pcf-debug -- /tmp/tv.pcf

Usage

pcf-debug <FILE> [SUBCOMMAND] [FLAGS]

SUBCOMMANDS:
  inspect   (default) byte map + layout + partition table + chain + diagnostics
  layout    physical region map only
  table     partition table only
  chain     table-block chain tree only
  hexdump   hexdump regions or an explicit byte range
  decode    run partition decoders and print field trees

GLOBAL FLAGS:
  --html <FILE>     also write a self-contained HTML report
  --no-color        disable ANSI colour (auto-off when stdout is not a TTY)
  --verify          compute and check hashes (default for inspect)
  --no-verify       skip hashing (fast path for large files)
  -h, --help        show help

HEXDUMP FLAGS:
  --region <NAME>   limit to regions matching a kind/label/uid substring
  --range <S[:L]>   hexdump bytes [S, S+L) (decimal or 0x-hex); L defaults to EOF
  --max-bytes <N>   cap bytes shown per region (default 512)

DECODE FLAGS:
  --uid <HEX>       decode only the partition whose UID starts with HEX
  --label <S>       decode only partitions whose label contains S
  --decoder <NAME>  force a decoder (e.g. pfs-node, pfs-session, raw)

Examples

# graphical HTML report
pcf-debug archive.pcf --html report.html

# hexdump just the data regions
pcf-debug archive.pcf hexdump --region data

# decode a PFS-MS file system image into field trees
pcf-debug fs.pcf decode

What it shows

  • Byte map — a proportional strip (text) or coloured bar (HTML) of every physical region: header, table-block headers, entry arrays, partition data, reserved slack, and gaps.
  • Partition table — type, UID, label, offsets, used/max/free, hash algorithm, and per-partition data-hash verification.
  • Block chain — each table block's offset, entry count, chain link, and table-hash status, including backward links and chain end.
  • Diagnostics — gaps, overlaps, truncated regions, chain cycles, and hash mismatches, by severity.
  • Decoded partitions — field trees produced by the plugin decoders.

Writing a decoder plugin

Decoders are registered statically (compiled in). A decoder turns a partition's raw bytes into a renderer-agnostic FieldNode tree that both the text and HTML renderers display.

  1. Implement the PartitionDecoder trait in a new module under src/plugin/:

    use crate::plugin::{Decoded, FieldNode, FieldValue, PartitionDecoder, PartitionMeta};
    
    pub struct MyDecoder;
    
    impl PartitionDecoder for MyDecoder {
        fn name(&self) -> &'static str { "my-format" }
    
        fn matches(&self, meta: &PartitionMeta, data: &[u8]) -> bool {
            meta.partition_type == 0x1234_5678 || data.starts_with(b"MYFMT")
        }
    
        fn decode(&self, _meta: &PartitionMeta, data: &[u8]) -> Decoded {
            let mut warnings = Vec::new();
            let mut fields = Vec::new();
            // ... read fields defensively; never panic ...
            fields.push(FieldNode::leaf(
                "magic",
                FieldValue::Text("MYFMT".into()),
                (0, 5),
            ));
            Decoded { format_name: "MY_FORMAT".into(), fields, warnings }
        }
    }
    
  2. Register it ahead of the raw fallback in DecoderRegistry::with_builtins (src/plugin/mod.rs), or at runtime with registry.register(Box::new(MyDecoder)).

The first decoder whose matches returns true wins; raw is always last and matches everything. decode must be infallible — on malformed input, return the fields you could read plus warnings.

The built-in pfs-node and pfs-session decoders (src/plugin/pfs.rs) are a complete worked example covering the PFS-MS record formats.

Tests

cargo test -p pcf-debug

Tests build the canonical 395-byte spec vector and hand-built PFS-MS records as fixtures, then assert the text snapshots, HTML structure, decoder field trees, and diagnostics for deliberately corrupted files.