pcf-debug 0.0.4

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`](../../reference/PCF-v1.0) 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):

```sh
# 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

```text
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

```sh
# 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/`:

   ```rust
   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.

[`PartitionDecoder`]: src/plugin/mod.rs
[`FieldNode`]: src/plugin/mod.rs

## Tests

```sh
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.