zipatch-rs 1.3.0

Parser for FFXIV ZiPatch patch files
Documentation
# zipatch-rs

[![Crates.io](https://img.shields.io/crates/v/zipatch-rs.svg)](https://crates.io/crates/zipatch-rs)
[![docs.rs](https://img.shields.io/docsrs/zipatch-rs)](https://docs.rs/zipatch-rs)
[![CI](https://github.com/reh3502/zipatch-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/reh3502/zipatch-rs/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/reh3502/zipatch-rs/graph/badge.svg)](https://codecov.io/gh/reh3502/zipatch-rs)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![MSRV: 1.85](https://img.shields.io/badge/rustc-1.85+-orange.svg)](https://blog.rust-lang.org/2025/02/20/Rust-1.85.0/)

A Rust library for parsing and applying FFXIV ZiPatch files (`.patch`).

ZiPatch is the binary patch format Square Enix ships Final Fantasy XIV game updates in. A patch is a chunked binary log of file additions, deletions, SqPack `.dat`/`.index` mutations, and binary diffs that an applier walks against an existing game installation to bring it to a new version.

## Quick start

```toml
[dependencies]
zipatch-rs = "1.3"
```

Apply a patch to an existing install:

```rust
use zipatch_rs::{ApplyContext, ZiPatchReader};

let reader = ZiPatchReader::from_path("D2024.05.31.0000.0000.patch")?;
let mut ctx = ApplyContext::new("/path/to/ffxiv/game");
reader.apply_to(&mut ctx)?;
```

See [docs.rs](https://docs.rs/zipatch-rs) for the indexed-apply path (`Plan` / `Verifier` / `IndexApplier`), observer hooks, and resume entry points.

## Features

- **Streaming chunk parser** — bounded memory, one chunk in flight at a time.
- **Sequential apply** — chunk-by-chunk, in-place writes to the install tree.
- **Indexed verify-and-repair** — build a `Plan` describing every byte of every target, verify an install against it, and re-apply only the regions that drift via `RepairManifest`.
- **Multi-patch chains** — a single `Plan` spans the patch chain end-to-end; mid-chain `RemoveAll` / `DeleteFile` / `AddFile @ 0` retroactively drop superseded regions.
- **Resumable apply** — checkpoint emission at every chunk and every DEFLATE block inside `SqpkFile::AddFile`; resume entry points fast-forward to the recorded position.
- **Progress + cancellation**`ApplyObserver` fires per chunk; `should_cancel` is polled mid-block on long DEFLATE streams.
- **Optional CRC32 verification**`Plan::compute_crc32` populates expected CRCs so the verifier catches single-byte damage the size-only policy would miss.
- **Alternate DEFLATE backends** — opt into `zlib-rs` or `zlib-ng` for faster DEFLATE; the default is pure-Rust `miniz_oxide` with no system dependencies.
- **Fuzz harness** — eight cargo-fuzz targets cover the full parse + apply surface; weekly cron in CI.

## Cargo features

| Feature | Default | Purpose |
|---------|---------|---------|
| `serde` | off | `Serialize` / `Deserialize` on `Plan`, `RepairManifest`, the `Checkpoint` types, and the `index` data model. The crate does not pick an on-disk format; you bring your own (`bincode`, `rmp-serde`, JSON, etc.). |
| `zlib-rs` | off | Switches the `SqpkFile::AddFile` decompress path to the pure-Rust [`zlib-rs`]https://github.com/trifectatechfoundation/zlib-rs port. Mutually exclusive with `zlib-ng`. |
| `zlib-ng` | off | Switches the decompress path to the C [`zlib-ng`]https://github.com/zlib-ng/zlib-ng fork. Requires `cmake` and a C compiler at build time. |
| `test-utils` | off | Exposes a `zipatch_rs::test_utils` module of chunk-framing fixtures used by the test suite and benchmarks. **Not part of the stable public API.** |

## Where it fits

`zipatch-rs` is a library, not a launcher. It does no network I/O — patch fetch, authentication, version negotiation, and progress UI all live somewhere else. The companion [`gaveloc`](https://github.com/reh3502/gaveloc) workspace is the full launcher product that consumes this crate as a path dependency.

If all you want to do is apply a `.patch` file you already have on disk to an install you already have on disk, this crate alone is enough.

## Reference implementation

The primary reference for the ZiPatch binary format is XIVLauncher's C# implementation in [`FFXIVQuickLauncher`](https://github.com/goatcorp/FFXIVQuickLauncher/tree/master/src/XIVLauncher.Common/Patching/ZiPatch). This crate is not a port — the parser and applier are independently implemented — but the C# tree is authoritative for chunk semantics when the spec is ambiguous.

## Stability

- **MSRV: Rust 1.85** (the minimum that supports edition 2024). Bumps land as a major version bump.
- **SemVer**: public API changes follow semantic versioning. The `test_utils` module is explicitly excluded.
- **License**: MIT — see [LICENSE]LICENSE.

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md). Security issues go through the [private advisory form](https://github.com/reh3502/zipatch-rs/security/advisories/new); see [SECURITY.md](SECURITY.md).

## Supported chunks

**Chunk types**

| Tag | Name | Notes |
|-----|------|-------|
| `FHDR` | File Header | V2 and V3 |
| `APLY` | Apply Option | ignore-missing, ignore-old-mismatch flags |
| `APFS` | Apply Free Space | |
| `ADIR` | Add Directory | |
| `DELD` | Delete Directory | |
| `SQPK` | SQPK command block | See sub-commands below |
| `EOF_` | End of File | |

**SQPK sub-commands**

| Command | Name | Notes |
|---------|------|-------|
| `A` | AddData | Writes data into a `.dat` file at a block offset |
| `D` | DeleteData | Zeros a block range in a `.dat` file |
| `E` | ExpandData | Expands a `.dat` file with an empty block |
| `H` | Header | Writes version or index headers into `.dat`/`.index` files |
| `F` | File | AddFile, DeleteFile, RemoveAll, MakeDirTree; supports DEFLATE-compressed blocks |
| `T` | TargetInfo | Platform (Win32, PS3, PS4), region, debug flag |
| `I` | Index | Parsed; no-op at apply time |
| `X` | PatchInfo | Parsed; no-op at apply time |