diff --git a/SabreTools.Library/External/CHDFile.cs b/SabreTools.Library/External/CHDFile.cs
index d93c2b90..a947db27 100644
--- a/SabreTools.Library/External/CHDFile.cs
+++ b/SabreTools.Library/External/CHDFile.cs
@@ -64,7 +64,7 @@ namespace SabreTools.Library.External
/// 0x68-0x7b - Parent SHA-1
/// ----------------------------------------------
///
- public class CHDFile
+ public class CHDFile : IDisposable
{
#region Private instance variables
@@ -82,20 +82,74 @@ namespace SabreTools.Library.External
private CHDCodecType[] m_compression = new CHDCodecType[4]; // array of compression types used
// map information
- private uint m_mapentrybytes; // length of each entry in a map
+ private uint m_mapentrybytes; // length of each entry in a map
+
+ // additional required vars
+ private BinaryReader m_br; // Binary reader representing the CHD stream
#endregion
#region Instance Methods
+ #region Constructors
+
+ ///
+ /// Create a new CHDFile from an input stream
+ ///
+ /// Stream representing the CHD file
+ public CHDFile(Stream chdstream)
+ {
+ BinaryReader br = new BinaryReader(chdstream);
+ }
+
+ ///
+ /// Dispose of the CHDFile
+ ///
+ public void Dispose()
+ {
+ m_br.Dispose();
+ }
+
+ #endregion
+
#region Header Parsing
+ ///
+ /// Validate the initial signature, version, and header size
+ ///
+ /// Unsigned int containing the version number, null if invalid
+ public uint? ValidateHeaderVersion()
+ {
+ // Read and verify the CHD signature
+ m_signature = m_br.ReadUInt64();
+ if (m_signature != Constants.CHDSignature)
+ {
+ // throw CHDERR_INVALID_FILE;
+ return null;
+ }
+
+ // Get the header size and version
+ m_headersize = m_br.ReadUInt32();
+ m_version = m_br.ReadUInt32();
+
+ // If we have an invalid combination of size and version
+ if ((m_version == 3 && m_headersize != Constants.CHD_V3_HEADER_SIZE)
+ || (m_version == 4 && m_headersize != Constants.CHD_V4_HEADER_SIZE)
+ || (m_version == 5 && m_headersize == Constants.CHD_V5_HEADER_SIZE)
+ || (m_version < 3 || m_version > 5))
+ {
+ // throw CHDERR_UNSUPPORTED_VERSION;
+ return null;
+ }
+
+ return m_version;
+ }
+
///
/// Parse a CHD v3 header
///
- /// Binary reader representing the input stream
/// The extracted SHA-1 on success, null otherwise
- public byte[] ParseCHDv3Header(BinaryReader br)
+ public byte[] ParseCHDv3Header()
{
// Set the blank SHA-1 hash
byte[] sha1 = new byte[20];
@@ -105,10 +159,10 @@ namespace SabreTools.Library.External
m_mapentrybytes = 16;
// Read the CHD flags
- uint flags = br.ReadUInt32();
+ uint flags = m_br.ReadUInt32();
// Determine compression
- switch (br.ReadUInt32())
+ switch (m_br.ReadUInt32())
{
case 0: m_compression[0] = CHDCodecType.CHD_CODEC_NONE; break;
case 1: m_compression[0] = CHDCodecType.CHD_CODEC_ZLIB; break;
@@ -119,15 +173,15 @@ namespace SabreTools.Library.External
m_compression[1] = m_compression[2] = m_compression[3] = CHDCodecType.CHD_CODEC_NONE;
- m_hunkcount = br.ReadUInt32();
- m_logicalbytes = br.ReadUInt64();
- m_metaoffset = br.ReadUInt64();
+ m_hunkcount = m_br.ReadUInt32();
+ m_logicalbytes = m_br.ReadUInt64();
+ m_metaoffset = m_br.ReadUInt64();
- br.BaseStream.Seek(76, SeekOrigin.Begin);
- m_hunkbytes = br.ReadUInt32();
+ m_br.BaseStream.Seek(76, SeekOrigin.Begin);
+ m_hunkbytes = m_br.ReadUInt32();
- br.BaseStream.Seek(Constants.CHDv3SHA1Offset, SeekOrigin.Begin);
- sha1 = br.ReadBytes(20);
+ m_br.BaseStream.Seek(Constants.CHDv3SHA1Offset, SeekOrigin.Begin);
+ sha1 = m_br.ReadBytes(20);
// guess at the units based on snooping the metadata
// m_unitbytes = guess_unitbytes();
@@ -139,9 +193,8 @@ namespace SabreTools.Library.External
///
/// Parse a CHD v4 header
///
- /// Binary reader representing the input stream
/// The extracted SHA-1 on success, null otherwise
- public byte[] ParseCHDv4Header(BinaryReader br)
+ public byte[] ParseCHDv4Header()
{
// Set the blank SHA-1 hash
byte[] sha1 = new byte[20];
@@ -151,10 +204,10 @@ namespace SabreTools.Library.External
m_mapentrybytes = 16;
// Read the CHD flags
- uint flags = br.ReadUInt32();
+ uint flags = m_br.ReadUInt32();
// Determine compression
- switch (br.ReadUInt32())
+ switch (m_br.ReadUInt32())
{
case 0: m_compression[0] = CHDCodecType.CHD_CODEC_NONE; break;
case 1: m_compression[0] = CHDCodecType.CHD_CODEC_ZLIB; break;
@@ -165,15 +218,15 @@ namespace SabreTools.Library.External
m_compression[1] = m_compression[2] = m_compression[3] = CHDCodecType.CHD_CODEC_NONE;
- m_hunkcount = br.ReadUInt32();
- m_logicalbytes = br.ReadUInt64();
- m_metaoffset = br.ReadUInt64();
+ m_hunkcount = m_br.ReadUInt32();
+ m_logicalbytes = m_br.ReadUInt64();
+ m_metaoffset = m_br.ReadUInt64();
- br.BaseStream.Seek(44, SeekOrigin.Begin);
- m_hunkbytes = br.ReadUInt32();
+ m_br.BaseStream.Seek(44, SeekOrigin.Begin);
+ m_hunkbytes = m_br.ReadUInt32();
- br.BaseStream.Seek(Constants.CHDv4SHA1Offset, SeekOrigin.Begin);
- sha1 = br.ReadBytes(20);
+ m_br.BaseStream.Seek(Constants.CHDv4SHA1Offset, SeekOrigin.Begin);
+ sha1 = m_br.ReadBytes(20);
// guess at the units based on snooping the metadata
// m_unitbytes = guess_unitbytes();
@@ -184,25 +237,24 @@ namespace SabreTools.Library.External
///
/// Parse a CHD v5 header
///
- /// Binary reader representing the input stream
/// The extracted SHA-1 on success, null otherwise
- public byte[] ParseCHDv5Header(BinaryReader br)
+ public byte[] ParseCHDv5Header()
{
// Set the blank SHA-1 hash
byte[] sha1 = new byte[20];
// Determine compression
- m_compression[0] = (CHDCodecType)br.ReadUInt32();
- m_compression[1] = (CHDCodecType)br.ReadUInt32();
- m_compression[2] = (CHDCodecType)br.ReadUInt32();
- m_compression[3] = (CHDCodecType)br.ReadUInt32();
+ m_compression[0] = (CHDCodecType)m_br.ReadUInt32();
+ m_compression[1] = (CHDCodecType)m_br.ReadUInt32();
+ m_compression[2] = (CHDCodecType)m_br.ReadUInt32();
+ m_compression[3] = (CHDCodecType)m_br.ReadUInt32();
- m_logicalbytes = br.ReadUInt64();
- m_mapoffset = br.ReadUInt64();
- m_metaoffset = br.ReadUInt64();
- m_hunkbytes = br.ReadUInt32();
+ m_logicalbytes = m_br.ReadUInt64();
+ m_mapoffset = m_br.ReadUInt64();
+ m_metaoffset = m_br.ReadUInt64();
+ m_hunkbytes = m_br.ReadUInt32();
m_hunkcount = (m_logicalbytes + m_hunkbytes - 1) / m_hunkbytes;
- m_unitbytes = br.ReadUInt32();
+ m_unitbytes = m_br.ReadUInt32();
m_unitcount = (m_logicalbytes + m_unitbytes - 1) / m_unitbytes;
// m_allow_writes = !compressed();
@@ -210,8 +262,8 @@ namespace SabreTools.Library.External
// determine properties of map entries
// m_mapentrybytes = compressed() ? 12 : 4;
- br.BaseStream.Seek(Constants.CHDv5SHA1Offset, SeekOrigin.Begin);
- sha1 = br.ReadBytes(20);
+ m_br.BaseStream.Seek(Constants.CHDv5SHA1Offset, SeekOrigin.Begin);
+ sha1 = m_br.ReadBytes(20);
return sha1;
}
@@ -270,47 +322,30 @@ namespace SabreTools.Library.External
Disk datItem = new Disk();
// Get a CHD object to store the data
- CHDFile chd = new CHDFile();
+ CHDFile chd = new CHDFile(fs);
- // Get a binary reader to make life easier
- BinaryReader br = new BinaryReader(fs);
-
- // Read and verify the CHD signature
- chd.m_signature = br.ReadUInt64();
- if (chd.m_signature != Constants.CHDSignature)
- {
- // throw CHDERR_INVALID_FILE;
- return null;
- }
-
- // Get the header size and version
- chd.m_headersize = br.ReadUInt32();
- chd.m_version = br.ReadUInt32();
+ // Get and validate the header version
+ uint? version = chd.ValidateHeaderVersion();
// Create a placeholder for the extracted SHA-1
byte[] sha1 = new byte[20];
- // If we have a CHD v3 file, parse it accordingly
- if (chd.m_headersize == Constants.CHD_V3_HEADER_SIZE && chd.m_version == 3)
+ // Now parse the rest of the header according to the version
+ switch (version)
{
- sha1 = chd.ParseCHDv3Header(br);
- }
- // If we have a CHD v4 file, parse it accordingly
- else if (chd.m_headersize == Constants.CHD_V4_HEADER_SIZE && chd.m_version == 4)
- {
- sha1 = chd.ParseCHDv4Header(br);
- }
- // If we have a CHD v5 file, parse it accordingly
- else if (chd.m_headersize == Constants.CHD_V5_HEADER_SIZE && chd.m_version == 5)
- {
- sha1 = chd.ParseCHDv5Header(br);
- }
- // If we don't have a valid combination, return null
- else
- {
- // throw CHDERR_UNSUPPORTED_VERSION;
- // throw CHDERR_INVALID_FILE;
- return null;
+ case 3:
+ sha1 = chd.ParseCHDv3Header();
+ break;
+ case 4:
+ sha1 = chd.ParseCHDv4Header();
+ break;
+ case 5:
+ sha1 = chd.ParseCHDv5Header();
+ break;
+ case null:
+ default:
+ // throw CHDERR_INVALID_FILE;
+ return null;
}
// Set the SHA-1 of the Disk to return