diff --git a/CHD/CompressedMapEntryV5.cs b/CHD/CompressedMapEntryV5.cs
new file mode 100644
index 0000000..d5437b8
--- /dev/null
+++ b/CHD/CompressedMapEntryV5.cs
@@ -0,0 +1,28 @@
+namespace SabreTools.Models.CHD
+{
+ ///
+ public class CompressedMapEntryV5
+ {
+ ///
+ /// Compression type
+ ///
+ public byte Compression { get; set; }
+
+ ///
+ /// Compressed length
+ ///
+ /// Actually UInt24
+ public uint CompLength { get; set; }
+
+ ///
+ /// Offset
+ ///
+ /// Actually UInt48
+ public ulong Offset { get; set; }
+
+ ///
+ /// CRC-16 of the data
+ ///
+ public ushort CRC { get; set; }
+ }
+}
diff --git a/CHD/CompressedMapHeaderV5.cs b/CHD/CompressedMapHeaderV5.cs
new file mode 100644
index 0000000..19f4447
--- /dev/null
+++ b/CHD/CompressedMapHeaderV5.cs
@@ -0,0 +1,42 @@
+namespace SabreTools.Models.CHD
+{
+ ///
+ public class CompressedMapHeaderV5
+ {
+ ///
+ /// Length of compressed map
+ ///
+ public uint Length { get; set; }
+
+ ///
+ /// Offset of first block
+ ///
+ /// Actually UInt48
+ public ulong DataStart { get; set; }
+
+ ///
+ /// CRC-16 of the map
+ ///
+ public ushort CRC { get; set; }
+
+ ///
+ /// Bits used to encode complength
+ ///
+ public byte LengthBits { get; set; }
+
+ ///
+ /// Bits used to encode self-refs
+ ///
+ public byte HunkBits { get; set; }
+
+ ///
+ /// Bits used to encode parent unit refs
+ ///
+ public byte ParentUnitBits { get; set; }
+
+ ///
+ /// Future use
+ ///
+ public byte Reserved { get; set; }
+ }
+}
diff --git a/CHD/Enums.cs b/CHD/Enums.cs
new file mode 100644
index 0000000..74dffe3
--- /dev/null
+++ b/CHD/Enums.cs
@@ -0,0 +1,42 @@
+using System;
+
+namespace SabreTools.Models.CHD
+{
+ ///
+ public enum CompressionType : uint
+ {
+ #region V1
+
+ CHDCOMPRESSION_NONE = 0,
+ CHDCOMPRESSION_ZLIB = 1,
+
+ #endregion
+
+ #region V3
+
+ CHDCOMPRESSION_ZLIB_PLUS = 2,
+
+ #endregion
+
+ #region V4
+
+ CHDCOMPRESSION_AV = 3,
+
+ #endregion
+ }
+
+ ///
+ [Flags]
+ public enum Flags : uint
+ {
+ ///
+ /// Set if this drive has a parent
+ ///
+ DriveHasParent = 0x00000001,
+
+ ///
+ /// Set if this drive allows writes
+ ///
+ DriveAllowsWrites = 0x00000002,
+ }
+}
\ No newline at end of file
diff --git a/CHD/Header.cs b/CHD/Header.cs
new file mode 100644
index 0000000..13b9e0d
--- /dev/null
+++ b/CHD/Header.cs
@@ -0,0 +1,25 @@
+namespace SabreTools.Models.CHD
+{
+ ///
+ public abstract class Header
+ {
+ ///
+ /// 'MComprHD'
+ ///
+#if NET48
+ public string Tag { get; set; }
+#else
+ public string? Tag { get; set; }
+#endif
+
+ ///
+ /// Length of header (including tag and length fields)
+ ///
+ public uint Length { get; set; }
+
+ ///
+ /// Drive format version
+ ///
+ public uint Version { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/CHD/HeaderV1.cs b/CHD/HeaderV1.cs
new file mode 100644
index 0000000..95a7f83
--- /dev/null
+++ b/CHD/HeaderV1.cs
@@ -0,0 +1,59 @@
+namespace SabreTools.Models.CHD
+{
+ ///
+ public class HeaderV1 : Header
+ {
+ ///
+ /// Flags
+ ///
+ public Flags Flags { get; set; }
+
+ ///
+ /// Compression type
+ ///
+ public CompressionType Compression { get; set; }
+
+ ///
+ /// 512-byte sectors per hunk
+ ///
+ public uint HunkSize { get; set; }
+
+ ///
+ /// Total # of hunks represented
+ ///
+ public uint TotalHunks { get; set; }
+
+ ///
+ /// Number of cylinders on hard disk
+ ///
+ public uint Cylinders { get; set; }
+
+ ///
+ /// Number of heads on hard disk
+ ///
+ public uint Heads { get; set; }
+
+ ///
+ /// Number of sectors on hard disk
+ ///
+ public uint Sectors { get; set; }
+
+ ///
+ /// MD5 checksum of raw data
+ ///
+#if NET48
+ public byte[] MD5 { get; set; } = new byte[16];
+#else
+ public byte[]? MD5 { get; set; } = new byte[16];
+#endif
+
+ ///
+ /// MD5 checksum of parent file
+ ///
+#if NET48
+ public byte[] ParentMD5 { get; set; } = new byte[16];
+#else
+ public byte[]? ParentMD5 { get; set; } = new byte[16];
+#endif
+ }
+}
diff --git a/CHD/HeaderV2.cs b/CHD/HeaderV2.cs
new file mode 100644
index 0000000..a77e246
--- /dev/null
+++ b/CHD/HeaderV2.cs
@@ -0,0 +1,64 @@
+namespace SabreTools.Models.CHD
+{
+ ///
+ public class HeaderV2 : Header
+ {
+ ///
+ /// Flags
+ ///
+ public Flags Flags { get; set; }
+
+ ///
+ /// Compression type
+ ///
+ public CompressionType Compression { get; set; }
+
+ ///
+ /// Seclen-byte sectors per hunk
+ ///
+ public uint HunkSize { get; set; }
+
+ ///
+ /// Total # of hunks represented
+ ///
+ public uint TotalHunks { get; set; }
+
+ ///
+ /// Number of cylinders on hard disk
+ ///
+ public uint Cylinders { get; set; }
+
+ ///
+ /// Number of heads on hard disk
+ ///
+ public uint Heads { get; set; }
+
+ ///
+ /// Number of sectors on hard disk
+ ///
+ public uint Sectors { get; set; }
+
+ ///
+ /// MD5 checksum of raw data
+ ///
+#if NET48
+ public byte[] MD5 { get; set; } = new byte[16];
+#else
+ public byte[]? MD5 { get; set; } = new byte[16];
+#endif
+
+ ///
+ /// MD5 checksum of parent file
+ ///
+#if NET48
+ public byte[] ParentMD5 { get; set; } = new byte[16];
+#else
+ public byte[]? ParentMD5 { get; set; } = new byte[16];
+#endif
+
+ ///
+ /// Number of bytes per sector
+ ///
+ public uint BytesPerSector { get; set; }
+ }
+}
diff --git a/CHD/HeaderV3.cs b/CHD/HeaderV3.cs
new file mode 100644
index 0000000..7292894
--- /dev/null
+++ b/CHD/HeaderV3.cs
@@ -0,0 +1,72 @@
+namespace SabreTools.Models.CHD
+{
+ ///
+ public class HeaderV3 : Header
+ {
+ ///
+ /// Flags
+ ///
+ public Flags Flags { get; set; }
+
+ ///
+ /// Compression type
+ ///
+ public CompressionType Compression { get; set; }
+
+ ///
+ /// Total # of hunks represented
+ ///
+ public uint TotalHunks { get; set; }
+
+ ///
+ /// Logical size of the data (in bytes)
+ ///
+ public ulong LogicalBytes { get; set; }
+
+ ///
+ /// Offset to the first blob of metadata
+ ///
+ public ulong MetaOffset { get; set; }
+
+ ///
+ /// MD5 checksum of raw data
+ ///
+#if NET48
+ public byte[] MD5 { get; set; } = new byte[16];
+#else
+ public byte[]? MD5 { get; set; } = new byte[16];
+#endif
+
+ ///
+ /// MD5 checksum of parent file
+ ///
+#if NET48
+ public byte[] ParentMD5 { get; set; } = new byte[16];
+#else
+ public byte[]? ParentMD5 { get; set; } = new byte[16];
+#endif
+
+ ///
+ /// Number of bytes per hunk
+ ///
+ public uint HunkBytes { get; set; }
+
+ ///
+ /// SHA1 checksum of raw data
+ ///
+#if NET48
+ public byte[] SHA1 { get; set; } = new byte[20];
+#else
+ public byte[]? SHA1 { get; set; } = new byte[20];
+#endif
+
+ ///
+ /// SHA1 checksum of parent file
+ ///
+#if NET48
+ public byte[] ParentSHA1 { get; set; } = new byte[20];
+#else
+ public byte[]? ParentSHA1 { get; set; } = new byte[20];
+#endif
+ }
+}
diff --git a/CHD/HeaderV4.cs b/CHD/HeaderV4.cs
new file mode 100644
index 0000000..cf8abf9
--- /dev/null
+++ b/CHD/HeaderV4.cs
@@ -0,0 +1,63 @@
+namespace SabreTools.Models.CHD
+{
+ ///
+ public class HeaderV4 : Header
+ {
+ ///
+ /// Flags
+ ///
+ public Flags Flags { get; set; }
+
+ ///
+ /// Compression type
+ ///
+ public CompressionType Compression { get; set; }
+
+ ///
+ /// Total # of hunks represented
+ ///
+ public uint TotalHunks { get; set; }
+
+ ///
+ /// Logical size of the data (in bytes)
+ ///
+ public ulong LogicalBytes { get; set; }
+
+ ///
+ /// Offset to the first blob of metadata
+ ///
+ public ulong MetaOffset { get; set; }
+
+ ///
+ /// Number of bytes per hunk
+ ///
+ public uint HunkBytes { get; set; }
+
+ ///
+ /// Combined raw+meta SHA1
+ ///
+#if NET48
+ public byte[] SHA1 { get; set; } = new byte[20];
+#else
+ public byte[]? SHA1 { get; set; } = new byte[20];
+#endif
+
+ ///
+ /// Combined raw+meta SHA1 of parent
+ ///
+#if NET48
+ public byte[] ParentSHA1 { get; set; } = new byte[20];
+#else
+ public byte[]? ParentSHA1 { get; set; } = new byte[20];
+#endif
+
+ ///
+ /// Raw data SHA1
+ ///
+#if NET48
+ public byte[] RawSHA1 { get; set; } = new byte[20];
+#else
+ public byte[]? RawSHA1 { get; set; } = new byte[20];
+#endif
+ }
+}
diff --git a/CHD/HeaderV5.cs b/CHD/HeaderV5.cs
new file mode 100644
index 0000000..970fa17
--- /dev/null
+++ b/CHD/HeaderV5.cs
@@ -0,0 +1,63 @@
+namespace SabreTools.Models.CHD
+{
+ ///
+ public class HeaderV5 : Header
+ {
+ ///
+ /// Which custom compressors are used?
+ ///
+ public uint[] Compressors { get; set; } = new uint[4];
+
+ ///
+ /// Logical size of the data (in bytes)
+ ///
+ public ulong LogicalBytes { get; set; }
+
+ ///
+ /// Offset to the map
+ ///
+ public ulong MapOffset { get; set; }
+
+ ///
+ /// Offset to the first blob of metadata
+ ///
+ public ulong MetaOffset { get; set; }
+
+ ///
+ /// Number of bytes per hunk (512k maximum)
+ ///
+ public uint HunkBytes { get; set; }
+
+ ///
+ /// Number of bytes per unit within each hunk
+ ///
+ public uint UnitBytes { get; set; }
+
+ ///
+ /// Raw data SHA1
+ ///
+#if NET48
+ public byte[] RawSHA1 { get; set; }
+#else
+ public byte[]? RawSHA1 { get; set; }
+#endif
+
+ ///
+ /// Combined raw+meta SHA1
+ ///
+#if NET48
+ public byte[] SHA1 { get; set; }
+#else
+ public byte[]? SHA1 { get; set; }
+#endif
+
+ ///
+ /// Combined raw+meta SHA1 of parent
+ ///
+#if NET48
+ public byte[] ParentSHA1 { get; set; }
+#else
+ public byte[]? ParentSHA1 { get; set; }
+#endif
+ }
+}
diff --git a/CHD/MapV1.cs b/CHD/MapV1.cs
new file mode 100644
index 0000000..bdb8451
--- /dev/null
+++ b/CHD/MapV1.cs
@@ -0,0 +1,16 @@
+namespace SabreTools.Models.CHD
+{
+ ///
+ public class MapV1
+ {
+ ///
+ /// Starting offset within the file
+ ///
+ public ulong StartingOffset { get; set; }
+
+ ///
+ /// Length of data; If == hunksize, data is uncompressed
+ ///
+ public ulong Length { get; set; }
+ }
+}
diff --git a/CHD/MapV3.cs b/CHD/MapV3.cs
new file mode 100644
index 0000000..9953ac6
--- /dev/null
+++ b/CHD/MapV3.cs
@@ -0,0 +1,31 @@
+namespace SabreTools.Models.CHD
+{
+ ///
+ public class MapV3
+ {
+ ///
+ /// Starting offset within the file
+ ///
+ public ulong StartingOffset { get; set; }
+
+ ///
+ /// 32-bit CRC of the uncompressed data
+ ///
+ public uint CRC32 { get; set; }
+
+ ///
+ /// Lower 16 bits of length
+ ///
+ public ushort LengthLo { get; set; }
+
+ ///
+ /// Upper 8 bits of length
+ ///
+ public byte LengthHi { get; set; }
+
+ ///
+ /// Flags, indicating compression info
+ ///
+ public byte Flags { get; set; }
+ }
+}
diff --git a/CHD/UncompressedMapV5.cs b/CHD/UncompressedMapV5.cs
new file mode 100644
index 0000000..4fcb346
--- /dev/null
+++ b/CHD/UncompressedMapV5.cs
@@ -0,0 +1,11 @@
+namespace SabreTools.Models.CHD
+{
+ ///
+ public class UncompressedMapV5
+ {
+ ///
+ /// Starting offset / hunk size
+ ///
+ public uint StartingOffset { get; set; }
+ }
+}