diff --git a/SabreTools.FileTypes/CHD/CHDFile.cs b/SabreTools.FileTypes/CHD/CHDFile.cs
index e2ceef34..08449739 100644
--- a/SabreTools.FileTypes/CHD/CHDFile.cs
+++ b/SabreTools.FileTypes/CHD/CHDFile.cs
@@ -2,6 +2,7 @@
using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
+using SabreTools.Models.CHD;
namespace SabreTools.FileTypes.CHD
{
@@ -9,16 +10,14 @@ namespace SabreTools.FileTypes.CHD
/// This is code adapted from chd.h and chd.cpp in MAME
/// Additional archival code from https://github.com/rtissera/libchdr/blob/master/src/chd.h
///
- public abstract class CHDFile : BaseFile
+ public class CHDFile : BaseFile
{
#region Private instance variables
- protected const string Signature = "MComprHD";
-
///
/// Model representing the correct CHD header
///
- protected Models.CHD.Header? _header;
+ protected Header? _header;
#endregion
@@ -48,11 +47,11 @@ namespace SabreTools.FileTypes.CHD
// Read and return the current CHD
return version switch
{
- 1 => CHDFileV1.Deserialize(stream),
- 2 => CHDFileV2.Deserialize(stream),
- 3 => CHDFileV3.Deserialize(stream),
- 4 => CHDFileV4.Deserialize(stream),
- 5 => CHDFileV5.Deserialize(stream),
+ 1 => DeserializeV1(stream),
+ 2 => DeserializeV2(stream),
+ 3 => DeserializeV3(stream),
+ 4 => DeserializeV4(stream),
+ 5 => DeserializeV5(stream),
_ => null,
};
}
@@ -64,6 +63,179 @@ namespace SabreTools.FileTypes.CHD
#endregion
+ #region Deserializers
+
+ ///
+ /// Parse and validate the header as if it's V1
+ ///
+ private static CHDFile? DeserializeV1(Stream stream)
+ {
+ var header = new HeaderV1();
+
+ byte[] tagBytes = stream.ReadBytes(8);
+ header.Tag = Encoding.ASCII.GetString(tagBytes);
+ if (header.Tag != Constants.SignatureString)
+ return null;
+
+ header.Length = stream.ReadUInt32BigEndian();
+ if (header.Length != Constants.HeaderV1Size)
+ return null;
+
+ header.Version = stream.ReadUInt32BigEndian();
+ header.Flags = (Flags)stream.ReadUInt32BigEndian();
+ header.Compression = (CompressionType)stream.ReadUInt32BigEndian();
+ if (header.Compression > CompressionType.CHDCOMPRESSION_ZLIB)
+ return null;
+
+ header.HunkSize = stream.ReadUInt32BigEndian();
+ header.TotalHunks = stream.ReadUInt32BigEndian();
+ header.Cylinders = stream.ReadUInt32BigEndian();
+ header.Heads = stream.ReadUInt32BigEndian();
+ header.Sectors = stream.ReadUInt32BigEndian();
+ header.MD5 = stream.ReadBytes(16);
+ header.ParentMD5 = stream.ReadBytes(16);
+
+ return new CHDFile { _header = header, MD5 = header.MD5 };
+ }
+
+ ///
+ /// Parse and validate the header as if it's V2
+ ///
+ private static CHDFile? DeserializeV2(Stream stream)
+ {
+ var header = new HeaderV2();
+
+ byte[] tagBytes = stream.ReadBytes(8);
+ header.Tag = Encoding.ASCII.GetString(tagBytes);
+ if (header.Tag != Constants.SignatureString)
+ return null;
+
+ header.Length = stream.ReadUInt32BigEndian();
+ if (header.Length != Constants.HeaderV2Size)
+ return null;
+
+ header.Version = stream.ReadUInt32BigEndian();
+ header.Flags = (Flags)stream.ReadUInt32BigEndian();
+ header.Compression = (CompressionType)stream.ReadUInt32BigEndian();
+ if (header.Compression > CompressionType.CHDCOMPRESSION_ZLIB)
+ return null;
+
+ header.HunkSize = stream.ReadUInt32BigEndian();
+ header.TotalHunks = stream.ReadUInt32BigEndian();
+ header.Cylinders = stream.ReadUInt32BigEndian();
+ header.Heads = stream.ReadUInt32BigEndian();
+ header.Sectors = stream.ReadUInt32BigEndian();
+ header.MD5 = stream.ReadBytes(16);
+ header.ParentMD5 = stream.ReadBytes(16);
+ header.BytesPerSector = stream.ReadUInt32BigEndian();
+
+ return new CHDFile { _header = header, MD5 = header.MD5 };
+ }
+
+ ///
+ /// Parse and validate the header as if it's V2
+ ///
+ private static CHDFile? DeserializeV3(Stream stream)
+ {
+ var header = new HeaderV3();
+
+ byte[] tagBytes = stream.ReadBytes(8);
+ header.Tag = Encoding.ASCII.GetString(tagBytes);
+ if (header.Tag != Constants.SignatureString)
+ return null;
+
+ header.Length = stream.ReadUInt32BigEndian();
+ if (header.Length != Constants.HeaderV3Size)
+ return null;
+
+ header.Version = stream.ReadUInt32BigEndian();
+ header.Flags = (Flags)stream.ReadUInt32BigEndian();
+ header.Compression = (CompressionType)stream.ReadUInt32BigEndian();
+ if (header.Compression > CompressionType.CHDCOMPRESSION_ZLIB_PLUS)
+ return null;
+
+ header.TotalHunks = stream.ReadUInt32BigEndian();
+ header.LogicalBytes = stream.ReadUInt64BigEndian();
+ header.MetaOffset = stream.ReadUInt64BigEndian();
+ header.MD5 = stream.ReadBytes(16);
+ header.ParentMD5 = stream.ReadBytes(16);
+ header.HunkBytes = stream.ReadUInt32BigEndian();
+ header.SHA1 = stream.ReadBytes(20);
+ header.ParentSHA1 = stream.ReadBytes(20);
+
+ return new CHDFile { _header = header, MD5 = header.MD5, SHA1 = header.SHA1 };
+ }
+
+ ///
+ /// Parse and validate the header as if it's V4
+ ///
+ private static CHDFile? DeserializeV4(Stream stream)
+ {
+ var header = new HeaderV4();
+
+ byte[] tagBytes = stream.ReadBytes(8);
+ header.Tag = Encoding.ASCII.GetString(tagBytes);
+ if (header.Tag != Constants.SignatureString)
+ return null;
+
+ header.Length = stream.ReadUInt32BigEndian();
+ if (header.Length != Constants.HeaderV4Size)
+ return null;
+
+ header.Version = stream.ReadUInt32BigEndian();
+ header.Flags = (Flags)stream.ReadUInt32BigEndian();
+ header.Compression = (CompressionType)stream.ReadUInt32BigEndian();
+ if (header.Compression > CompressionType.CHDCOMPRESSION_AV)
+ return null;
+
+ header.TotalHunks = stream.ReadUInt32BigEndian();
+ header.LogicalBytes = stream.ReadUInt64BigEndian();
+ header.MetaOffset = stream.ReadUInt64BigEndian();
+ header.HunkBytes = stream.ReadUInt32BigEndian();
+ header.SHA1 = stream.ReadBytes(20);
+ header.ParentSHA1 = stream.ReadBytes(20);
+ header.RawSHA1 = stream.ReadBytes(20);
+
+ return new CHDFile { _header = header, SHA1 = header.SHA1 };
+ }
+
+ ///
+ /// Parse and validate the header as if it's V5
+ ///
+ private static CHDFile? DeserializeV5(Stream stream)
+ {
+ var header = new HeaderV5();
+
+ byte[] tagBytes = stream.ReadBytes(8);
+ header.Tag = Encoding.ASCII.GetString(tagBytes);
+ if (header.Tag != Constants.SignatureString)
+ return null;
+
+ header.Length = stream.ReadUInt32BigEndian();
+ if (header.Length != Constants.HeaderV5Size)
+ return null;
+
+ header.Version = stream.ReadUInt32BigEndian();
+ header.Compressors = new uint[4];
+ for (int i = 0; i < header.Compressors.Length; i++)
+ {
+ header.Compressors[i] = stream.ReadUInt32BigEndian();
+ }
+
+ header.LogicalBytes = stream.ReadUInt64BigEndian();
+ header.MapOffset = stream.ReadUInt64BigEndian();
+ header.MetaOffset = stream.ReadUInt64BigEndian();
+ header.HunkBytes = stream.ReadUInt32BigEndian();
+ header.UnitBytes = stream.ReadUInt32BigEndian();
+ header.RawSHA1 = stream.ReadBytes(20);
+ header.SHA1 = stream.ReadBytes(20);
+ header.ParentSHA1 = stream.ReadBytes(20);
+
+ return new CHDFile { _header = header, SHA1 = header.SHA1 };
+ }
+
+ #endregion
+
#region Helpers
///
@@ -82,17 +254,17 @@ namespace SabreTools.FileTypes.CHD
stream.SeekIfPossible();
// Check the signature
- if (!string.Equals(tag, Signature, StringComparison.Ordinal))
+ if (!string.Equals(tag, Constants.SignatureString, StringComparison.Ordinal))
return 0;
// Match the version to header length
- return version switch
+ return (version, length) switch
{
- 1 => length == CHDFileV1.HeaderSize ? version : 0,
- 2 => length == CHDFileV2.HeaderSize ? version : 0,
- 3 => length == CHDFileV3.HeaderSize ? version : 0,
- 4 => length == CHDFileV4.HeaderSize ? version : 0,
- 5 => length == CHDFileV5.HeaderSize ? version : 0,
+ (1, Constants.HeaderV1Size) => version,
+ (2, Constants.HeaderV2Size) => version,
+ (3, Constants.HeaderV3Size) => version,
+ (4, Constants.HeaderV4Size) => version,
+ (5, Constants.HeaderV5Size) => version,
_ => 0,
};
}
diff --git a/SabreTools.FileTypes/CHD/CHDFileV1.cs b/SabreTools.FileTypes/CHD/CHDFileV1.cs
deleted file mode 100644
index e5a782bd..00000000
--- a/SabreTools.FileTypes/CHD/CHDFileV1.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System.IO;
-using System.Text;
-using SabreTools.IO.Extensions;
-using SabreTools.Models.CHD;
-
-namespace SabreTools.FileTypes.CHD
-{
- ///
- /// CHD V1 File
- ///
- public class CHDFileV1 : CHDFile
- {
- internal const int HeaderSize = 76;
-
- ///
- /// Parse and validate the header as if it's V1
- ///
- internal static CHDFileV1? Deserialize(Stream stream)
- {
- var header = new HeaderV1();
-
- byte[] tagBytes = stream.ReadBytes(8);
- header.Tag = Encoding.ASCII.GetString(tagBytes);
- if (header.Tag != Signature)
- return null;
-
- header.Length = stream.ReadUInt32BigEndian();
- if (header.Length != HeaderSize)
- return null;
-
- header.Version = stream.ReadUInt32BigEndian();
- header.Flags = (Flags)stream.ReadUInt32BigEndian();
- header.Compression = (CompressionType)stream.ReadUInt32BigEndian();
- if (header.Compression > CompressionType.CHDCOMPRESSION_ZLIB)
- return null;
-
- header.HunkSize = stream.ReadUInt32BigEndian();
- header.TotalHunks = stream.ReadUInt32BigEndian();
- header.Cylinders = stream.ReadUInt32BigEndian();
- header.Heads = stream.ReadUInt32BigEndian();
- header.Sectors = stream.ReadUInt32BigEndian();
- header.MD5 = stream.ReadBytes(16);
- header.ParentMD5 = stream.ReadBytes(16);
-
- return new CHDFileV1 { _header = header, MD5 = header.MD5 };
- }
- }
-}
diff --git a/SabreTools.FileTypes/CHD/CHDFileV2.cs b/SabreTools.FileTypes/CHD/CHDFileV2.cs
deleted file mode 100644
index a4f51916..00000000
--- a/SabreTools.FileTypes/CHD/CHDFileV2.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using System.IO;
-using System.Text;
-using SabreTools.IO.Extensions;
-using SabreTools.Models.CHD;
-
-namespace SabreTools.FileTypes.CHD
-{
- ///
- /// CHD V2 File
- ///
- public class CHDFileV2 : CHDFile
- {
- internal const int HeaderSize = 80;
-
- ///
- /// Parse and validate the header as if it's V2
- ///
- internal static CHDFileV2? Deserialize(Stream stream)
- {
- var header = new HeaderV2();
-
- byte[] tagBytes = stream.ReadBytes(8);
- header.Tag = Encoding.ASCII.GetString(tagBytes);
- if (header.Tag != Signature)
- return null;
-
- header.Length = stream.ReadUInt32BigEndian();
- if (header.Length != HeaderSize)
- return null;
-
- header.Version = stream.ReadUInt32BigEndian();
- header.Flags = (Flags)stream.ReadUInt32BigEndian();
- header.Compression = (CompressionType)stream.ReadUInt32BigEndian();
- if (header.Compression > CompressionType.CHDCOMPRESSION_ZLIB)
- return null;
-
- header.HunkSize = stream.ReadUInt32BigEndian();
- header.TotalHunks = stream.ReadUInt32BigEndian();
- header.Cylinders = stream.ReadUInt32BigEndian();
- header.Heads = stream.ReadUInt32BigEndian();
- header.Sectors = stream.ReadUInt32BigEndian();
- header.MD5 = stream.ReadBytes(16);
- header.ParentMD5 = stream.ReadBytes(16);
- header.BytesPerSector = stream.ReadUInt32BigEndian();
-
- return new CHDFileV2 { _header = header, MD5 = header.MD5 };
- }
- }
-}
diff --git a/SabreTools.FileTypes/CHD/CHDFileV3.cs b/SabreTools.FileTypes/CHD/CHDFileV3.cs
deleted file mode 100644
index d0fe05b4..00000000
--- a/SabreTools.FileTypes/CHD/CHDFileV3.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using System.IO;
-using System.Text;
-using SabreTools.IO.Extensions;
-using SabreTools.Models.CHD;
-
-namespace SabreTools.FileTypes.CHD
-{
- ///
- /// CHD V3 File
- ///
- public class CHDFileV3 : CHDFile
- {
- internal const int HeaderSize = 120;
-
- ///
- /// Parse and validate the header as if it's V3
- ///
- internal static CHDFileV3? Deserialize(Stream stream)
- {
- var header = new HeaderV3();
-
- byte[] tagBytes = stream.ReadBytes(8);
- header.Tag = Encoding.ASCII.GetString(tagBytes);
- if (header.Tag != Signature)
- return null;
-
- header.Length = stream.ReadUInt32BigEndian();
- if (header.Length != HeaderSize)
- return null;
-
- header.Version = stream.ReadUInt32BigEndian();
- header.Flags = (Flags)stream.ReadUInt32BigEndian();
- header.Compression = (CompressionType)stream.ReadUInt32BigEndian();
- if (header.Compression > CompressionType.CHDCOMPRESSION_ZLIB_PLUS)
- return null;
-
- header.TotalHunks = stream.ReadUInt32BigEndian();
- header.LogicalBytes = stream.ReadUInt64BigEndian();
- header.MetaOffset = stream.ReadUInt64BigEndian();
- header.MD5 = stream.ReadBytes(16);
- header.ParentMD5 = stream.ReadBytes(16);
- header.HunkBytes = stream.ReadUInt32BigEndian();
- header.SHA1 = stream.ReadBytes(20);
- header.ParentSHA1 = stream.ReadBytes(20);
-
- return new CHDFileV3 { _header = header, MD5 = header.MD5, SHA1 = header.SHA1 };
- }
- }
-}
diff --git a/SabreTools.FileTypes/CHD/CHDFileV4.cs b/SabreTools.FileTypes/CHD/CHDFileV4.cs
deleted file mode 100644
index b4fec45d..00000000
--- a/SabreTools.FileTypes/CHD/CHDFileV4.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System.IO;
-using System.Text;
-using SabreTools.IO.Extensions;
-using SabreTools.Models.CHD;
-
-namespace SabreTools.FileTypes.CHD
-{
- ///
- /// CHD V4 File
- ///
- public class CHDFileV4 : CHDFile
- {
- internal const int HeaderSize = 108;
-
- ///
- /// Parse and validate the header as if it's V4
- ///
- internal static CHDFileV4? Deserialize(Stream stream)
- {
- var header = new HeaderV4();
-
- byte[] tagBytes = stream.ReadBytes(8);
- header.Tag = Encoding.ASCII.GetString(tagBytes);
- if (header.Tag != Signature)
- return null;
-
- header.Length = stream.ReadUInt32BigEndian();
- if (header.Length != HeaderSize)
- return null;
-
- header.Version = stream.ReadUInt32BigEndian();
- header.Flags = (Flags)stream.ReadUInt32BigEndian();
- header.Compression = (CompressionType)stream.ReadUInt32BigEndian();
- if (header.Compression > CompressionType.CHDCOMPRESSION_AV)
- return null;
-
- header.TotalHunks = stream.ReadUInt32BigEndian();
- header.LogicalBytes = stream.ReadUInt64BigEndian();
- header.MetaOffset = stream.ReadUInt64BigEndian();
- header.HunkBytes = stream.ReadUInt32BigEndian();
- header.SHA1 = stream.ReadBytes(20);
- header.ParentSHA1 = stream.ReadBytes(20);
- header.RawSHA1 = stream.ReadBytes(20);
-
- return new CHDFileV4 { _header = header, SHA1 = header.SHA1 };
- }
- }
-}
diff --git a/SabreTools.FileTypes/CHD/CHDFileV5.cs b/SabreTools.FileTypes/CHD/CHDFileV5.cs
deleted file mode 100644
index a32445fd..00000000
--- a/SabreTools.FileTypes/CHD/CHDFileV5.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using System.IO;
-using System.Text;
-using SabreTools.IO.Extensions;
-using SabreTools.Models.CHD;
-
-namespace SabreTools.FileTypes.CHD
-{
- ///
- /// CHD V5 File
- ///
- public class CHDFileV5 : CHDFile
- {
- internal const int HeaderSize = 124;
-
- ///
- /// Parse and validate the header as if it's V5
- ///
- internal static CHDFileV5? Deserialize(Stream stream)
- {
- var header = new HeaderV5();
-
- byte[] tagBytes = stream.ReadBytes(8);
- header.Tag = Encoding.ASCII.GetString(tagBytes);
- if (header.Tag != Signature)
- return null;
-
- header.Length = stream.ReadUInt32BigEndian();
- if (header.Length != HeaderSize)
- return null;
-
- header.Version = stream.ReadUInt32BigEndian();
- header.Compressors = new uint[4];
- for (int i = 0; i < header.Compressors.Length; i++)
- {
- header.Compressors[i] = stream.ReadUInt32BigEndian();
- }
-
- header.LogicalBytes = stream.ReadUInt64BigEndian();
- header.MapOffset = stream.ReadUInt64BigEndian();
- header.MetaOffset = stream.ReadUInt64BigEndian();
- header.HunkBytes = stream.ReadUInt32BigEndian();
- header.UnitBytes = stream.ReadUInt32BigEndian();
- header.RawSHA1 = stream.ReadBytes(20);
- header.SHA1 = stream.ReadBytes(20);
- header.ParentSHA1 = stream.ReadBytes(20);
-
- return new CHDFileV5 { _header = header, SHA1 = header.SHA1 };
- }
- }
-}
diff --git a/SabreTools.FileTypes/CHD/Constants.cs b/SabreTools.FileTypes/CHD/Constants.cs
new file mode 100644
index 00000000..6629efae
--- /dev/null
+++ b/SabreTools.FileTypes/CHD/Constants.cs
@@ -0,0 +1,17 @@
+namespace SabreTools.FileTypes.CHD
+{
+ internal static class Constants
+ {
+ public const string SignatureString = "MComprHD";
+
+ #region Header Sizes
+
+ public const int HeaderV1Size = 76;
+ public const int HeaderV2Size = 80;
+ public const int HeaderV3Size = 120;
+ public const int HeaderV4Size = 108;
+ public const int HeaderV5Size = 124;
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/SabreTools.Test/DatItems/DatItemTests.cs b/SabreTools.Test/DatItems/DatItemTests.cs
index 0eb7015d..8e05c2cf 100644
--- a/SabreTools.Test/DatItems/DatItemTests.cs
+++ b/SabreTools.Test/DatItems/DatItemTests.cs
@@ -159,7 +159,7 @@ namespace SabreTools.Test.DatItems
{
FileType.Folder => new Folder(),
FileType.AaruFormat => new AaruFormat(),
- FileType.CHD => new CHDFileV5(),
+ FileType.CHD => new CHDFile(),
FileType.ZipArchive => new ZipArchive(),
_ => new BaseFile(),
};