-
Notifications
You must be signed in to change notification settings - Fork 0
parent child chds
CHD supports delta (incremental) images: a child CHD stores only the hunks that differ from its parent; identical hunks become parent references. This is how MAME ships multi-disc or regional variants without duplicating identical data.
- The child header stores the parent's
md5(V1–V3) and/orsha1(V3–V5) hashes. - Map entries of type
PARENTpoint into the parent's data instead of local storage. - In V1–V4, a parent reference is a direct hunk index into the parent.
- In V5, a parent reference is a unit index (unit =
hunkbytes / unitbytessubdivision, e.g. 512-byte sectors inside 4096-byte hunks). References can be unaligned — a hunk may need the tail of parent hunk N and the head of parent hunk N+1; CHDSharp stitches the two halves.
child.chd parent.chd
┌───────────────────┐ ┌───────────────────┐
│ hunk 0: compressed│ │ hunk 0: data │
│ hunk 1: PARENT→u4 │──────────▶│ hunk 1: data │
│ hunk 2: compressed│ │ hunk 2: data │
│ hunk 3: PARENT→u7 │──────┐ │ hunk 3: data │
└───────────────────┘ └───▶│ hunk 4: data │
└───────────────────┘
Three ways, matching the three Open overloads:
// 1. Path-based: the library opens the parent and owns it.
var err = ChdFile.Open("child.chd", "parent.chd", out var child);
using (child) { ... }
// 2. External parent instance: caller keeps ownership, may share it.
ChdFile.Open("parent.chd", out var parent);
using (parent)
{
foreach (var childPath in new[] { "child1.chd", "child2.chd" })
{
ChdFile.Open(childPath, parent, out var c);
using (c) { /* read hunks; parent hunks resolve through `parent` */ }
}
}
// 3. From streams.
using var childStream = File.OpenRead("child.chd");
ChdFile.Open(childStream, leaveOpen: false, parent, out var child2);Async twins exist for all three (OpenAsync).
| Situation | Result |
|---|---|
| Child opened without a parent | Chderrrequiresparent |
Supplied parent's md5/sha1 does not match the child's stored parent hashes |
Chderrinvalidparent |
| Parent-referenced hunk read when no parent is attached |
Chderrrequiresparent (from ReadHunk) |
Parent validation happens at open time: the child's stored parentmd5/parentsha1 is compared against the actual parent's Md5/Sha1 (when both are non-empty).
var result = Chd.CheckFileWithParent("child.chd", "parent.chd");
if (result.IsSuccess)
Console.WriteLine($"child V{result.Version} verified against parent");CheckFileWithParent decompresses the child and the referenced parent hunks and validates every hash — this is the single-threaded counterpart of CheckFile (which is standalone-only).
From the consumer's point of view, reading is transparent:
var err = ChdFile.Open("child.chd", "parent.chd", out var child);
using (child)
{
var hunk = new byte[child.HunkBytes];
for (uint i = 0; i < child.HunkCount; i++)
{
var herr = child.ReadHunk(i, hunk); // local or parent data, same call
if (herr != ChdError.Chderrnone) break;
}
}Read, ReadAllBytes, EnumerateHunks, and extraction all work identically on child CHDs.
ReadHunk resolves a map entry as follows:
-
PARENT→ReadParentHunk:- V1–V4 (and uncompressed V5 maps): direct parent hunk index.
- V5 compressed maps: convert the unit index to parent hunk(s):
- aligned → one parent hunk;
- unaligned → two adjacent parent hunks stitched at the unit boundary.
-
SELF→ follow the self-reference to the entry that holds real data (and use the decompressed cache when the block is repeated). - Otherwise → read local compressed bytes (stream or
Precachebuffer) and decompress.
The child keeps a reference to the parent (_parent); when the child was opened with a parent path, it owns the parent and disposes it together with itself.
CHDSharp
Format & internals
API & usage
CLI & writing
Parity & validation
Operations
Reference