using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace BizHawk.Emulation.DiscSystem
{
///
/// Represents a volume descriptor for a disk image.
///
public class ISOVolumeDescriptor
{
#region Constants
///
/// We are handling the parsing by reading the entire header and
/// extracting the appropriate bytes.
///
/// This is done for performance reasons.
///
private const int LENGTH_SHORT_IDENTIFIER = 32;
private const int LENGTH_IDENTIFIER = 37;
private const int LENGTH_LONG_IDENTIFIER = 128;
private const int LENGTH_ROOT_DIRECTORY_RECORD = 34;
private const int LENGTH_TIME = 17;
private const int LENGTH_RESERVED = 512;
#endregion
#region Public Properties
///
/// The type of this volume description, only 1 and 255 are supported
///
public byte Type;
///
/// The system identifier
///
public byte[] SystemIdentifier;
///
/// The volume identifier
///
public byte[] VolumeIdentifier;
///
/// The number of sectors on the disk
///
public int NumberOfSectors;
///
/// Volume Set Size (should be 1)
///
public int VolumeSetSize;
///
/// Volume Sequence Number (should be 1)
///
public int VolumeSequenceNumber;
///
/// Sector Size (should be 2048)
///
public int SectorSize;
///
/// Size of the path table
///
public int PathTableSize;
///
/// Sector offset of the first path table
///
public int OffsetOfFirstLittleEndianPathTable;
///
/// Sector offset of the second path table
///
public int OffsetOfSecondLittleEndianPathTable;
///
/// Sector offset of the first path table
///
public int OffsetOfFirstBigEndianPathTable;
///
/// Sector offset of the second path table
///
public int OffsetOfSecondBigEndianPathTable;
///
/// The root directory record
///
public ISONodeRecord RootDirectoryRecord;
///
/// The volumen set identifier
///
public byte[] VolumeSetIdentifier;
///
/// The publisher identifier
///
public byte[] PublisherIdentifier;
///
/// The data preparer identifier
///
public byte[] DataPreparerIdentifier;
///
/// The application identifier
///
public byte[] ApplicationIdentifier;
///
/// The copyright identifier
///
public byte[] CopyrightFileIdentifier;
///
/// The abstract file identifier
///
public byte[] AbstractFileIdentifier;
///
/// The bibliographical file identifier
///
public byte[] BibliographicalFileIdentifier;
///
/// The time and date the volume was created
///
public byte[] VolumeCreationDateTime;
///
/// The time and date the volume was last modified
///
public byte[] LastModifiedDateTime;
///
/// The time and date the volume expires
///
public byte[] ExpirationDateTime;
///
/// The time and data when the volume is effective
///
public byte[] EffectiveDateTime;
///
/// Extra reserved data
///
public byte[] Reserved;
#endregion
#region Construction
///
/// Constructor.
///
public ISOVolumeDescriptor()
{
// Set everything to the default value
this.Type = 0;
this.SystemIdentifier = new byte[LENGTH_SHORT_IDENTIFIER];
this.VolumeIdentifier = new byte[LENGTH_SHORT_IDENTIFIER];
this.NumberOfSectors = 0;
this.VolumeSetSize = 1;
this.VolumeSequenceNumber = 1;
this.SectorSize = ISOFile.SECTOR_SIZE;
this.PathTableSize = 0;
this.OffsetOfFirstLittleEndianPathTable = 0;
this.OffsetOfSecondLittleEndianPathTable = 0;
this.OffsetOfFirstBigEndianPathTable = 0;
this.OffsetOfSecondBigEndianPathTable = 0;
this.RootDirectoryRecord = new ISONodeRecord();
this.VolumeSetIdentifier = new byte[LENGTH_LONG_IDENTIFIER];
this.PublisherIdentifier = new byte[LENGTH_LONG_IDENTIFIER];
this.DataPreparerIdentifier = new byte[LENGTH_LONG_IDENTIFIER];
this.ApplicationIdentifier = new byte[LENGTH_LONG_IDENTIFIER];
this.CopyrightFileIdentifier = new byte[LENGTH_IDENTIFIER];
this.AbstractFileIdentifier = new byte[LENGTH_IDENTIFIER];
this.BibliographicalFileIdentifier = new byte[LENGTH_IDENTIFIER];
this.VolumeCreationDateTime = new byte[LENGTH_TIME];
this.LastModifiedDateTime = new byte[LENGTH_TIME];
this.ExpirationDateTime = new byte[LENGTH_TIME];
this.EffectiveDateTime = new byte[LENGTH_TIME];
this.Reserved = new byte[LENGTH_RESERVED];
}
#endregion
#region Parsing
///
/// Parse the volume descriptor header.
///
/// The stream to parse from.
public bool Parse(Stream s)
{
EndianBitConverter bc = EndianBitConverter.CreateForLittleEndian();
EndianBitConverter bcBig = EndianBitConverter.CreateForBigEndian();
long startPosition = s.Position;
byte[] buffer = new byte[ISOFile.SECTOR_SIZE];
// Read the entire structure
s.Read(buffer, 0, ISOFile.SECTOR_SIZE);
// Get the type
this.Type = buffer[0];
//zero 24-jun-2013 - validate
// "CD001" + 0x01
if (buffer[1] == 'C' && buffer[2] == 'D' && buffer[3] == '0' && buffer[4] == '0' && buffer[5] == '1' && buffer[6] == 0x01)
{
//it seems to be a valid volume descriptor
}
else
{
return false;
}
// Handle the primary volume information
if (this.Type == 1)
{
int cursor = 8;
// Get the system identifier
Array.Copy(buffer, cursor,
this.SystemIdentifier, 0, LENGTH_SHORT_IDENTIFIER);
cursor += LENGTH_SHORT_IDENTIFIER;
// Get the volume identifier
Array.Copy(buffer, cursor,
this.VolumeIdentifier, 0, LENGTH_SHORT_IDENTIFIER);
cursor += LENGTH_SHORT_IDENTIFIER;
cursor += 8;
// Get the total number of sectors
this.NumberOfSectors = bc.ToInt32(buffer, cursor);
cursor += 8;
cursor += 32;
this.VolumeSetSize = bc.ToInt16(buffer, cursor);
cursor += 4;
this.VolumeSequenceNumber = bc.ToInt16(buffer, cursor);
cursor += 4;
this.SectorSize = bc.ToInt16(buffer, cursor);
cursor += 4;
this.PathTableSize = bc.ToInt32(buffer, cursor);
cursor += 8;
this.OffsetOfFirstLittleEndianPathTable = bc.ToInt32(buffer, cursor);
cursor += 4;
this.OffsetOfSecondLittleEndianPathTable = bc.ToInt32(buffer, cursor);
cursor += 4;
this.OffsetOfFirstLittleEndianPathTable = bcBig.ToInt32(buffer, cursor);
cursor += 4;
this.OffsetOfSecondLittleEndianPathTable = bcBig.ToInt32(buffer, cursor);
cursor += 4;
this.RootDirectoryRecord.Parse(buffer, cursor);
cursor += LENGTH_ROOT_DIRECTORY_RECORD;
Array.Copy(buffer, cursor,
this.VolumeSetIdentifier, 0, LENGTH_LONG_IDENTIFIER);
cursor += LENGTH_LONG_IDENTIFIER;
Array.Copy(buffer, cursor,
this.PublisherIdentifier, 0, LENGTH_LONG_IDENTIFIER);
cursor += LENGTH_LONG_IDENTIFIER;
Array.Copy(buffer, cursor,
this.DataPreparerIdentifier, 0, LENGTH_LONG_IDENTIFIER);
cursor += LENGTH_LONG_IDENTIFIER;
Array.Copy(buffer, cursor,
this.ApplicationIdentifier, 0, LENGTH_LONG_IDENTIFIER);
cursor += LENGTH_LONG_IDENTIFIER;
Array.Copy(buffer, cursor,
this.CopyrightFileIdentifier, 0, LENGTH_IDENTIFIER);
cursor += LENGTH_IDENTIFIER;
Array.Copy(buffer, cursor,
this.AbstractFileIdentifier, 0, LENGTH_IDENTIFIER);
cursor += LENGTH_IDENTIFIER;
Array.Copy(buffer, cursor,
this.BibliographicalFileIdentifier, 0, LENGTH_IDENTIFIER);
cursor += LENGTH_IDENTIFIER;
Array.Copy(buffer, cursor,
this.VolumeCreationDateTime, 0, LENGTH_TIME);
cursor += LENGTH_TIME;
Array.Copy(buffer, cursor,
this.LastModifiedDateTime, 0, LENGTH_TIME);
cursor += LENGTH_TIME;
Array.Copy(buffer, cursor,
this.ExpirationDateTime, 0, LENGTH_TIME);
cursor += LENGTH_TIME;
Array.Copy(buffer, cursor,
this.EffectiveDateTime, 0, LENGTH_TIME);
cursor += LENGTH_TIME;
cursor += 1;
cursor += 1;
Array.Copy(buffer, cursor,
this.Reserved, 0, LENGTH_RESERVED);
cursor += LENGTH_RESERVED;
}
return true;
}
#endregion
#region Type Information
///
/// Returns true if this is the terminator volume descriptor.
///
/// True if the terminator.
public bool IsTerminator()
{
return (this.Type == 255);
}
#endregion
}
}