Merge pull request #1020 from Asnivor/CDFS
DiscSystem - CDFS classes now consume CD-I discs
This commit is contained in:
commit
3e3a64fffe
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.DiscSystem
|
namespace BizHawk.Emulation.DiscSystem
|
||||||
|
@ -114,5 +115,49 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
// (asni 20171013) - Some methods I wrote that have been shoehorned in from another project to speed up development time
|
||||||
|
// If these are offensive in any way, tell me I suck and that I need to do more work with existing methods
|
||||||
|
#region Misc
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a byte array of any length
|
||||||
|
/// Not really anything endian going on, but I struggled to find a better place for it
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buffer"></param>
|
||||||
|
/// <param name="offset"></param>
|
||||||
|
/// <param name="length"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public byte[] ReadBytes(byte[] buffer, int offset, int length)
|
||||||
|
{
|
||||||
|
return buffer.Skip(offset).Take(length).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an int32 value from any size byte array
|
||||||
|
/// (careful, data may/will be truncated)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buffer"></param>
|
||||||
|
/// <param name="offset"></param>
|
||||||
|
/// <param name="length"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public int ReadIntValue(byte[] buffer, int offset, int length)
|
||||||
|
{
|
||||||
|
var bytes = buffer.Skip(offset).Take(length).ToArray();
|
||||||
|
|
||||||
|
if (swap)
|
||||||
|
Array.Reverse(bytes);
|
||||||
|
|
||||||
|
if (length == 1)
|
||||||
|
return bytes.FirstOrDefault();
|
||||||
|
|
||||||
|
if (length == 2)
|
||||||
|
return BitConverter.ToInt16(bytes, 0);
|
||||||
|
|
||||||
|
int result = BitConverter.ToInt32(bytes, 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,10 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
|
|
||||||
// Read the record
|
// Read the record
|
||||||
record = new ISONodeRecord();
|
record = new ISONodeRecord();
|
||||||
record.Parse(s);
|
if (ISOFile.Format == ISOFile.ISOFormat.CDInteractive)
|
||||||
|
record.ParseCDInteractive(s);
|
||||||
|
if (ISOFile.Format == ISOFile.ISOFormat.ISO9660)
|
||||||
|
record.ParseISO9660(s);
|
||||||
|
|
||||||
|
|
||||||
//zero 24-jun-2013 - improved validity checks
|
//zero 24-jun-2013 - improved validity checks
|
||||||
|
@ -95,6 +98,7 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the node as a child
|
// Add the node as a child
|
||||||
|
if (!this.Children.ContainsKey(record.Name))
|
||||||
this.Children.Add(record.Name, node);
|
this.Children.Add(record.Name, node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,18 +2,20 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.DiscSystem
|
namespace BizHawk.Emulation.DiscSystem
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This class is meant to parse disk images as specified by ISO9660.
|
/// This class is meant to parse disk images as specified by:
|
||||||
/// Specifically, it should work for most disk images that are created
|
///
|
||||||
|
/// ISO9660
|
||||||
|
/// -------
|
||||||
|
/// It should work for most disk images that are created
|
||||||
/// by the stanard disk imaging software. This class is by no means
|
/// by the stanard disk imaging software. This class is by no means
|
||||||
/// robust to all variations of ISO9660.
|
/// robust to all variations of ISO9660.
|
||||||
/// Also, this class does not currently support the UDF file system.
|
/// Also, this class does not currently support the UDF file system.
|
||||||
///
|
///
|
||||||
/// TODO: Add functions to enumerate a directory or visit a file...
|
|
||||||
///
|
|
||||||
/// The information for building class came from three primary sources:
|
/// The information for building class came from three primary sources:
|
||||||
/// 1. The ISO9660 wikipedia article:
|
/// 1. The ISO9660 wikipedia article:
|
||||||
/// http://en.wikipedia.org/wiki/ISO_9660
|
/// http://en.wikipedia.org/wiki/ISO_9660
|
||||||
|
@ -21,6 +23,28 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
/// http://alumnus.caltech.edu/~pje/iso9660.html
|
/// http://alumnus.caltech.edu/~pje/iso9660.html
|
||||||
/// 3. The ISO 9660 File System
|
/// 3. The ISO 9660 File System
|
||||||
/// http://users.telenet.be/it3.consultants.bvba/handouts/ISO9960.html
|
/// http://users.telenet.be/it3.consultants.bvba/handouts/ISO9960.html
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// CD-I
|
||||||
|
/// ----
|
||||||
|
/// (asni - 20171013) - Class modified to be able to detect and consume Green
|
||||||
|
/// Book disc images.
|
||||||
|
///
|
||||||
|
/// The implemtation of CD-I in this class adds some (but not all) additional
|
||||||
|
/// properties to the class structures that CD-I brings. This means that
|
||||||
|
/// the same ISO class structures can be returned for both standards.
|
||||||
|
/// These small additions are readily found in ISOVolumeDescriptor.cs
|
||||||
|
///
|
||||||
|
/// ISOFile.cs also now contains a public 'ISOFormat' enum that is set
|
||||||
|
/// during disc parsing.
|
||||||
|
///
|
||||||
|
/// The main reference source for this implementation:
|
||||||
|
/// 1. The CD-I Full Functional Specification (aka Green Book)
|
||||||
|
/// https://www.lscdweb.com/data/downloadables/2/8/cdi_may94_r2.pdf
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// TODO: Add functions to enumerate a directory or visit a file...
|
||||||
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ISOFile
|
public class ISOFile
|
||||||
{
|
{
|
||||||
|
@ -33,6 +57,17 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Static Members
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Making this a static for now. Every other way I tried was fairly ineligant (asni)
|
||||||
|
/// </summary>
|
||||||
|
public static ISOFormat Format;
|
||||||
|
|
||||||
|
public static List<CDIPathNode> CDIPathTable;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Public Members
|
#region Public Members
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -46,6 +81,11 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ISODirectoryNode Root;
|
public ISODirectoryNode Root;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The type of CDFS format detected
|
||||||
|
/// </summary>
|
||||||
|
public ISOFormat CDFSType;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Construction
|
#region Construction
|
||||||
|
@ -87,6 +127,8 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
bool isValid = desc.Parse(s);
|
bool isValid = desc.Parse(s);
|
||||||
if (!isValid) return false;
|
if (!isValid) return false;
|
||||||
|
|
||||||
|
this.CDFSType = Format;
|
||||||
|
|
||||||
if (desc.IsTerminator())
|
if (desc.IsTerminator())
|
||||||
break;
|
break;
|
||||||
else if (desc.Type < 4)
|
else if (desc.Type < 4)
|
||||||
|
@ -106,6 +148,7 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
// return;
|
// return;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
|
||||||
//zero 24-jun-2013 - if theres no volume descriptors, we're gonna call this not a cdfs
|
//zero 24-jun-2013 - if theres no volume descriptors, we're gonna call this not a cdfs
|
||||||
if (VolumeDescriptors.Count == 0) return false;
|
if (VolumeDescriptors.Count == 0) return false;
|
||||||
|
|
||||||
|
@ -116,12 +159,68 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
|
|
||||||
// Create (and visit) the root node
|
// Create (and visit) the root node
|
||||||
this.Root = new ISODirectoryNode(this.VolumeDescriptors[0].RootDirectoryRecord);
|
this.Root = new ISODirectoryNode(this.VolumeDescriptors[0].RootDirectoryRecord);
|
||||||
|
|
||||||
visitedNodes.Add(this.Root.Offset, this.Root);
|
visitedNodes.Add(this.Root.Offset, this.Root);
|
||||||
this.Root.Parse(s, visitedNodes);
|
this.Root.Parse(s, visitedNodes);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private List<KeyValuePair<string, ISOFileNode>> fileNodes;
|
||||||
|
private List<ISODirectoryNode> dirsParsed;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a flat list of all recursed files
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public List<KeyValuePair<string, ISOFileNode>> EnumerateAllFilesRecursively()
|
||||||
|
{
|
||||||
|
fileNodes = new List<KeyValuePair<string, ISOFileNode>>();
|
||||||
|
dirsParsed = new List<ISODirectoryNode>();
|
||||||
|
|
||||||
|
if (Root.Children == null)
|
||||||
|
return fileNodes;
|
||||||
|
|
||||||
|
// get all folders
|
||||||
|
List<KeyValuePair<string, ISONode>> dirs = (from a in Root.Children
|
||||||
|
where a.Value.GetType() == typeof(ISODirectoryNode)
|
||||||
|
select a).ToList();
|
||||||
|
// iterate through each folder
|
||||||
|
foreach (var d in dirs)
|
||||||
|
{
|
||||||
|
// process all files in this directory (and recursively process files in sub folders
|
||||||
|
ISODirectoryNode idn = d.Value as ISODirectoryNode;
|
||||||
|
if (dirsParsed.Where(a => a == idn).Count() > 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
dirsParsed.Add(idn);
|
||||||
|
ProcessDirectoryFiles(idn.Children);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileNodes.Distinct().ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessDirectoryFiles(Dictionary<string, ISONode> idn)
|
||||||
|
{
|
||||||
|
foreach (var n in idn)
|
||||||
|
{
|
||||||
|
if (n.Value.GetType() == typeof(ISODirectoryNode))
|
||||||
|
{
|
||||||
|
if (dirsParsed.Where(a => a == n.Value).Count() > 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
dirsParsed.Add(n.Value as ISODirectoryNode);
|
||||||
|
ProcessDirectoryFiles((n.Value as ISODirectoryNode).Children);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
KeyValuePair<string, ISOFileNode> f = new KeyValuePair<string, ISOFileNode>(n.Key, n.Value as ISOFileNode);
|
||||||
|
fileNodes.Add(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Printing
|
#region Printing
|
||||||
|
@ -136,5 +235,16 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Misc
|
||||||
|
|
||||||
|
public enum ISOFormat
|
||||||
|
{
|
||||||
|
Unknown,
|
||||||
|
ISO9660,
|
||||||
|
CDInteractive
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,12 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte Length;
|
public byte Length;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is the number of blocks at the beginning of the file reserved for extended attribute information
|
||||||
|
/// The format of the extended attribute record is not defined and is reserved for application use
|
||||||
|
/// </summary>
|
||||||
|
public byte ExtendedAttribRecordLength;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The file offset of the data for this file/directory (in sectors).
|
/// The file offset of the data for this file/directory (in sectors).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -148,14 +154,19 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
// Put the array into a memory stream and pass to the main parsing function
|
// Put the array into a memory stream and pass to the main parsing function
|
||||||
MemoryStream s = new MemoryStream(data);
|
MemoryStream s = new MemoryStream(data);
|
||||||
s.Seek(cursor, SeekOrigin.Begin);
|
s.Seek(cursor, SeekOrigin.Begin);
|
||||||
this.Parse(s);
|
|
||||||
|
if (ISOFile.Format == ISOFile.ISOFormat.ISO9660)
|
||||||
|
this.ParseISO9660(s);
|
||||||
|
|
||||||
|
if (ISOFile.Format == ISOFile.ISOFormat.CDInteractive)
|
||||||
|
this.ParseCDInteractive(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parse the node record from the given stream.
|
/// Parse the node record from the given ISO9660 stream.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="s">The stream to parse from.</param>
|
/// <param name="s">The stream to parse from.</param>
|
||||||
public void Parse(Stream s)
|
public void ParseISO9660(Stream s)
|
||||||
{
|
{
|
||||||
EndianBitConverter bc = EndianBitConverter.CreateForLittleEndian();
|
EndianBitConverter bc = EndianBitConverter.CreateForLittleEndian();
|
||||||
long startPosition = s.Position;
|
long startPosition = s.Position;
|
||||||
|
@ -212,6 +223,100 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
s.Seek(startPosition + this.Length, SeekOrigin.Begin);
|
s.Seek(startPosition + this.Length, SeekOrigin.Begin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parse the node record from the given CD-I stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="s">The stream to parse from.</param>
|
||||||
|
public void ParseCDInteractive(Stream s)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
BP Size in bytes Description
|
||||||
|
1 1 Record length
|
||||||
|
2 1 Extended Attribute record length
|
||||||
|
3 4 Reserved
|
||||||
|
7 4 File beginning LBN
|
||||||
|
11 4 Reserved
|
||||||
|
15 4 File size
|
||||||
|
19 6 Creation date
|
||||||
|
25 1 Reserved
|
||||||
|
26 1 File flags
|
||||||
|
27 2 Interleave
|
||||||
|
29 2 Reserved
|
||||||
|
31 2 Album Set Sequence number
|
||||||
|
33 1 File name size
|
||||||
|
34 (n) File name
|
||||||
|
34+n 4 Owner ID
|
||||||
|
38+n 2 Attributes
|
||||||
|
40+n 2 Reserved
|
||||||
|
42+n 1 File number
|
||||||
|
43+n 1 Reserved
|
||||||
|
43+n Total
|
||||||
|
*/
|
||||||
|
|
||||||
|
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);
|
||||||
|
s.Position -= ISOFile.SECTOR_SIZE;
|
||||||
|
|
||||||
|
// Get the record length
|
||||||
|
this.Length = buffer[0];
|
||||||
|
|
||||||
|
// extended attribute record length
|
||||||
|
this.ExtendedAttribRecordLength = buffer[1];
|
||||||
|
|
||||||
|
// Read Data Offset
|
||||||
|
this.OffsetOfData = bcBig.ReadIntValue(buffer, 6, 4);
|
||||||
|
|
||||||
|
// Read Data Length
|
||||||
|
this.LengthOfData = bcBig.ReadIntValue(buffer, 14, 4);
|
||||||
|
|
||||||
|
// Read the time
|
||||||
|
var ti = bc.ReadBytes(buffer, 18, 6);
|
||||||
|
this.Year = ti[0];
|
||||||
|
this.Month = ti[1];
|
||||||
|
this.Day = ti[2];
|
||||||
|
this.Hour = ti[3];
|
||||||
|
this.Minute = ti[4];
|
||||||
|
this.Second = ti[5];
|
||||||
|
|
||||||
|
// read interleave - still to do
|
||||||
|
|
||||||
|
// read album (volume) set sequence number (we are ignoring this)
|
||||||
|
|
||||||
|
// Read the name length
|
||||||
|
this.NameLength = buffer[32];
|
||||||
|
|
||||||
|
// Read the file/directory name
|
||||||
|
var name = bc.ReadBytes(buffer, 33, this.NameLength);
|
||||||
|
if (this.NameLength == 1 && (name[0] == 0 || name[0] == 1))
|
||||||
|
{
|
||||||
|
if (name[0] == 0)
|
||||||
|
this.Name = ISONodeRecord.CURRENT_DIRECTORY;
|
||||||
|
else
|
||||||
|
this.Name = ISONodeRecord.PARENT_DIRECTORY;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.Name = ASCIIEncoding.ASCII.GetString(name, 0, this.NameLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip ownerID for now
|
||||||
|
|
||||||
|
// read the flags - only really interested in the directory attribute (bit 15)
|
||||||
|
// (confusingly these are called 'attributes' in CD-I. the CD-I 'File Flags' entry is something else entirely)
|
||||||
|
this.Flags = buffer[37 + this.NameLength];
|
||||||
|
|
||||||
|
// skip filenumber
|
||||||
|
//this.FileNumber = buffer[41 + this.NameLength];
|
||||||
|
|
||||||
|
// Seek to end
|
||||||
|
s.Seek(startPosition + this.Length, SeekOrigin.Begin);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,13 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Private Properties
|
||||||
|
|
||||||
|
private EndianBitConverter bc = EndianBitConverter.CreateForLittleEndian();
|
||||||
|
private EndianBitConverter bcBig = EndianBitConverter.CreateForBigEndian();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Public Properties
|
#region Public Properties
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -66,19 +73,19 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int PathTableSize;
|
public int PathTableSize;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sector offset of the first path table
|
/// (ISO9660 only) Sector offset of the first path table
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int OffsetOfFirstLittleEndianPathTable;
|
public int OffsetOfFirstLittleEndianPathTable;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sector offset of the second path table
|
/// (ISO9660 only) Sector offset of the second path table
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int OffsetOfSecondLittleEndianPathTable;
|
public int OffsetOfSecondLittleEndianPathTable;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sector offset of the first path table
|
/// (ISO9660 only) Sector offset of the first path table
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int OffsetOfFirstBigEndianPathTable;
|
public int OffsetOfFirstBigEndianPathTable;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sector offset of the second path table
|
/// (ISO9660 only) Sector offset of the second path table
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int OffsetOfSecondBigEndianPathTable;
|
public int OffsetOfSecondBigEndianPathTable;
|
||||||
|
|
||||||
|
@ -135,10 +142,34 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
public byte[] EffectiveDateTime;
|
public byte[] EffectiveDateTime;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extra reserved data
|
/// (ISO9660 only) Extra reserved data
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte[] Reserved;
|
public byte[] Reserved;
|
||||||
|
|
||||||
|
|
||||||
|
// CD-Interactive only
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The bits of this field are numbered from 0 to 7 starting with the least significant bit
|
||||||
|
/// BitPosition 0: A value of 0 = the coded character set identifier field specifies only an escape sequence registered according to ISO 2375
|
||||||
|
/// A value of 1 = the coded character set identifier field specifies only an escape sequence NOT registered according to ISO 2375
|
||||||
|
/// BitPostion 1-7: All bits are 0 (reserved for future standardization)
|
||||||
|
/// </summary>
|
||||||
|
public byte VolumeFlags;
|
||||||
|
/// <summary>
|
||||||
|
/// This field specifies one escape sequence according to the International Register of Coded Character Sets to be used with escape
|
||||||
|
/// sequence standards for recording.The ESC character, which is the first character of all sequences, shall be omitted when recording this field
|
||||||
|
/// </summary>
|
||||||
|
public byte[] CodedCharSetIdent;
|
||||||
|
/// <summary>
|
||||||
|
/// The block address of the first block of the system Path Table is kept in this field
|
||||||
|
/// </summary>
|
||||||
|
public int AddressOfPathTable;
|
||||||
|
/// <summary>
|
||||||
|
/// This number is used to indicate the revision number of the file structure standard to which the
|
||||||
|
/// directory search files conform. It is set to one
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Construction
|
#region Construction
|
||||||
|
@ -183,6 +214,11 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
this.EffectiveDateTime = new byte[LENGTH_TIME];
|
this.EffectiveDateTime = new byte[LENGTH_TIME];
|
||||||
|
|
||||||
this.Reserved = new byte[LENGTH_RESERVED];
|
this.Reserved = new byte[LENGTH_RESERVED];
|
||||||
|
|
||||||
|
// CD-I specific
|
||||||
|
this.VolumeFlags = 0;
|
||||||
|
this.CodedCharSetIdent = new byte[LENGTH_SHORT_IDENTIFIER];
|
||||||
|
this.AddressOfPathTable = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -190,33 +226,45 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
#region Parsing
|
#region Parsing
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parse the volume descriptor header.
|
/// Start parsing the volume descriptor header.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="s">The stream to parse from.</param>
|
/// <param name="s">The stream to parse from.</param>
|
||||||
public bool Parse(Stream s)
|
public bool Parse(Stream s)
|
||||||
{
|
{
|
||||||
EndianBitConverter bc = EndianBitConverter.CreateForLittleEndian();
|
|
||||||
EndianBitConverter bcBig = EndianBitConverter.CreateForBigEndian();
|
|
||||||
long startPosition = s.Position;
|
long startPosition = s.Position;
|
||||||
byte[] buffer = new byte[ISOFile.SECTOR_SIZE];
|
byte[] buffer = new byte[ISOFile.SECTOR_SIZE];
|
||||||
|
|
||||||
// Read the entire structure
|
// Read the entire structure
|
||||||
s.Read(buffer, 0, ISOFile.SECTOR_SIZE);
|
s.Read(buffer, 0, ISOFile.SECTOR_SIZE);
|
||||||
|
|
||||||
// Get the type
|
// Parse based on format
|
||||||
this.Type = buffer[0];
|
byte[] header = bc.ReadBytes(buffer, 0, ISOFile.SECTOR_SIZE);
|
||||||
|
if (GetISO9660(header))
|
||||||
//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
|
ParseISO9660(s);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
else
|
if (GetCDI(header))
|
||||||
{
|
{
|
||||||
|
ParseCDInteractive(s);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ParseISO9660(Stream s)
|
||||||
|
{
|
||||||
|
long startPosition = s.Position;
|
||||||
|
byte[] buffer = new byte[ISOFile.SECTOR_SIZE];
|
||||||
|
s.Position = startPosition - ISOFile.SECTOR_SIZE;
|
||||||
|
|
||||||
|
// Read the entire structure
|
||||||
|
s.Read(buffer, 0, ISOFile.SECTOR_SIZE);
|
||||||
|
|
||||||
|
// Get the type
|
||||||
|
this.Type = buffer[0];
|
||||||
|
|
||||||
// Handle the primary volume information
|
// Handle the primary volume information
|
||||||
if (this.Type == 1)
|
if (this.Type == 1)
|
||||||
{
|
{
|
||||||
|
@ -304,10 +352,147 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
this.Reserved, 0, LENGTH_RESERVED);
|
this.Reserved, 0, LENGTH_RESERVED);
|
||||||
cursor += LENGTH_RESERVED;
|
cursor += LENGTH_RESERVED;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ParseCDInteractive(Stream s)
|
||||||
|
{
|
||||||
|
/* From the Green Book Spec
|
||||||
|
* BP (byte position) obviously is n+1
|
||||||
|
|
||||||
|
BP Size in Bytes Description
|
||||||
|
1 1 Disc Label Record Type
|
||||||
|
2 5 Volume Structure Standard ID
|
||||||
|
7 1 Volume Structure Version number
|
||||||
|
8 1 Volume flags
|
||||||
|
9 32 System identifier
|
||||||
|
41 32 Volume identifier
|
||||||
|
73 12 Reserved
|
||||||
|
85 4 Volume space size
|
||||||
|
89 32 Coded Character Set identifier
|
||||||
|
121 2 Reserved
|
||||||
|
123 2 Number of Volumes in Album
|
||||||
|
125 2 Reserved
|
||||||
|
127 2 Album Set Sequence number
|
||||||
|
129 2 Reserved
|
||||||
|
131 2 Logical Block size
|
||||||
|
133 4 Reserved
|
||||||
|
137 4 Path Table size
|
||||||
|
141 8 Reserved
|
||||||
|
149 4 Address of Path Table
|
||||||
|
153 38 Reserved
|
||||||
|
191 128 Album identifier
|
||||||
|
319 128 Publisher identifier
|
||||||
|
447 128 Data Preparer identifier
|
||||||
|
575 128 Application identifier
|
||||||
|
703 32 Copyright file name
|
||||||
|
735 5 Reserved
|
||||||
|
740 32 Abstract file name
|
||||||
|
772 5 Reserved
|
||||||
|
777 32 Bibliographic file name
|
||||||
|
809 5 Reserved
|
||||||
|
814 16 Creation date and time
|
||||||
|
830 1 Reserved
|
||||||
|
831 16 Modification date and time
|
||||||
|
847 1 Reserved
|
||||||
|
848 16 Expiration date and time
|
||||||
|
864 1 Reserved
|
||||||
|
865 16 Effective date and time
|
||||||
|
881 1 Reserved
|
||||||
|
882 1 File Structure Standard Version number
|
||||||
|
883 1 Reserved
|
||||||
|
884 512 Application use
|
||||||
|
1396 653 Reserved */
|
||||||
|
|
||||||
|
long startPosition = s.Position;
|
||||||
|
byte[] buffer = new byte[ISOFile.SECTOR_SIZE];
|
||||||
|
s.Position = startPosition - ISOFile.SECTOR_SIZE;
|
||||||
|
|
||||||
|
// Read the entire structure
|
||||||
|
s.Read(buffer, 0, ISOFile.SECTOR_SIZE);
|
||||||
|
|
||||||
|
// Get the type
|
||||||
|
this.Type = buffer[0];
|
||||||
|
|
||||||
|
// Handle the primary volume information
|
||||||
|
if (this.Type == 1)
|
||||||
|
{
|
||||||
|
this.VolumeFlags = buffer[7];
|
||||||
|
this.SystemIdentifier = bc.ReadBytes(buffer, 8, LENGTH_SHORT_IDENTIFIER);
|
||||||
|
this.VolumeIdentifier = bc.ReadBytes(buffer, 40, LENGTH_SHORT_IDENTIFIER);
|
||||||
|
this.NumberOfSectors = bcBig.ReadIntValue(buffer, 84, 4);
|
||||||
|
this.CodedCharSetIdent = bc.ReadBytes(buffer, 88, LENGTH_SHORT_IDENTIFIER);
|
||||||
|
this.VolumeSetSize = bcBig.ReadIntValue(buffer, 122, 2);
|
||||||
|
this.VolumeSequenceNumber = bcBig.ReadIntValue(buffer, 126, 2);
|
||||||
|
this.SectorSize = bcBig.ReadIntValue(buffer, 130, 2);
|
||||||
|
this.PathTableSize = bcBig.ReadIntValue(buffer, 136, 4);
|
||||||
|
this.AddressOfPathTable = bcBig.ReadIntValue(buffer, 148, 4);
|
||||||
|
|
||||||
|
this.VolumeSetIdentifier = bc.ReadBytes(buffer, 190, LENGTH_LONG_IDENTIFIER);
|
||||||
|
this.PublisherIdentifier = bc.ReadBytes(buffer, 318, LENGTH_LONG_IDENTIFIER);
|
||||||
|
this.DataPreparerIdentifier = bc.ReadBytes(buffer, 446, LENGTH_LONG_IDENTIFIER);
|
||||||
|
this.ApplicationIdentifier = bc.ReadBytes(buffer, 574, LENGTH_LONG_IDENTIFIER);
|
||||||
|
|
||||||
|
this.CopyrightFileIdentifier = bc.ReadBytes(buffer, 702, LENGTH_SHORT_IDENTIFIER);
|
||||||
|
this.AbstractFileIdentifier = bc.ReadBytes(buffer, 739, LENGTH_SHORT_IDENTIFIER);
|
||||||
|
this.BibliographicalFileIdentifier = bc.ReadBytes(buffer, 776, LENGTH_SHORT_IDENTIFIER);
|
||||||
|
|
||||||
|
this.VolumeCreationDateTime = bc.ReadBytes(buffer, 813, 16);
|
||||||
|
this.LastModifiedDateTime = bc.ReadBytes(buffer, 830, 16);
|
||||||
|
this.ExpirationDateTime = bc.ReadBytes(buffer, 847, 16);
|
||||||
|
this.EffectiveDateTime = bc.ReadBytes(buffer, 864, 16);
|
||||||
|
|
||||||
|
// save current position
|
||||||
|
long pos = s.Position;
|
||||||
|
|
||||||
|
// get path table records
|
||||||
|
s.Position = ISOFile.SECTOR_SIZE * this.AddressOfPathTable;
|
||||||
|
ISOFile.CDIPathTable = CDIPathNode.ParsePathTable(s, this.PathTableSize);
|
||||||
|
|
||||||
|
// read the root dir record
|
||||||
|
s.Position = ISOFile.SECTOR_SIZE * ISOFile.CDIPathTable[0].DirectoryBlockAddress;
|
||||||
|
s.Read(buffer, 0, ISOFile.SECTOR_SIZE);
|
||||||
|
this.RootDirectoryRecord.Parse(buffer, 0);
|
||||||
|
|
||||||
|
// go back to where we were
|
||||||
|
s.Position = pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Detect ISO9660
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buffer"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public bool GetISO9660(byte[] buffer)
|
||||||
|
{
|
||||||
|
//zero 24-jun-2013 - validate ISO9660
|
||||||
|
// "CD001" + 0x01
|
||||||
|
if (buffer[1] == 'C' && buffer[2] == 'D' && buffer[3] == '0' && buffer[4] == '0' && buffer[5] == '1' && buffer[6] == 0x01)
|
||||||
|
{
|
||||||
|
ISOFile.Format = ISOFile.ISOFormat.ISO9660;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Detect CD-I
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buffer"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public bool GetCDI(byte[] buffer)
|
||||||
|
{
|
||||||
|
// CD-Interactive
|
||||||
|
if (Encoding.ASCII.GetString(bc.ReadBytes(buffer, 1, 5)).Contains("CD-I"))
|
||||||
|
{
|
||||||
|
ISOFile.Format = ISOFile.ISOFormat.CDInteractive;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Type Information
|
#region Type Information
|
||||||
|
@ -321,6 +506,111 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
return (this.Type == 255);
|
return (this.Type == 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a Directory Path Table entry on a CD-I disc
|
||||||
|
/// </summary>
|
||||||
|
public class CDIPathNode
|
||||||
|
{
|
||||||
|
#region Public Properties
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The length of the directory name.
|
||||||
|
/// </summary>
|
||||||
|
public byte NameLength;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is the length of the Extended Attribute record
|
||||||
|
/// </summary>
|
||||||
|
public byte ExtendedAttribRecordLength;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This field contains the beginning logical block number (LBN) of the directory file on disc
|
||||||
|
/// </summary>
|
||||||
|
public int DirectoryBlockAddress;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is the number (relative to the beginning of the Path Table) of this directory's parent
|
||||||
|
/// </summary>
|
||||||
|
public int ParentDirectoryNumber;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The directory name.
|
||||||
|
/// This variable length field is used to store the actual text representing the name of the directory.
|
||||||
|
/// If the length of the file name is odd, a null padding byte is added to make the size of the Path Table record even.
|
||||||
|
/// The padding byte is not included in the name size field.
|
||||||
|
/// </summary>
|
||||||
|
public string Name;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Construction
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Empty Constructor
|
||||||
|
/// </summary>
|
||||||
|
public CDIPathNode()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Parsing
|
||||||
|
|
||||||
|
/*
|
||||||
|
BP Size in bytes Description
|
||||||
|
1 1 Name size
|
||||||
|
2 1 Extended Attribute record length
|
||||||
|
3 4 Directory block address
|
||||||
|
7 2 Parent Directory number
|
||||||
|
9 n Directory file name
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static List<CDIPathNode> ParsePathTable(Stream s, int PathTableSize)
|
||||||
|
{
|
||||||
|
EndianBitConverter bc = EndianBitConverter.CreateForLittleEndian();
|
||||||
|
EndianBitConverter bcBig = EndianBitConverter.CreateForBigEndian();
|
||||||
|
|
||||||
|
byte[] buffer = new byte[ISOFile.SECTOR_SIZE];
|
||||||
|
|
||||||
|
// Read the entire structure
|
||||||
|
s.Read(buffer, 0, ISOFile.SECTOR_SIZE);
|
||||||
|
|
||||||
|
int startCursor = 0;
|
||||||
|
|
||||||
|
List<CDIPathNode> pathNodes = new List<CDIPathNode>();
|
||||||
|
|
||||||
|
int pad = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
CDIPathNode node = new CDIPathNode();
|
||||||
|
byte[] data = bc.ReadBytes(buffer, startCursor, ISOFile.SECTOR_SIZE - startCursor);
|
||||||
|
node.NameLength = data[0];
|
||||||
|
|
||||||
|
node.ExtendedAttribRecordLength = data[1];
|
||||||
|
node.DirectoryBlockAddress = bcBig.ReadIntValue(data, 2, 4);
|
||||||
|
node.ParentDirectoryNumber = bcBig.ReadIntValue(data, 6, 2);
|
||||||
|
node.Name = Encoding.ASCII.GetString(bc.ReadBytes(data, 8, data[0]));
|
||||||
|
|
||||||
|
// if nameLength is odd a padding byte must be added
|
||||||
|
|
||||||
|
if (node.NameLength % 2 != 0)
|
||||||
|
pad = 1;
|
||||||
|
|
||||||
|
pathNodes.Add(node);
|
||||||
|
|
||||||
|
startCursor += node.NameLength + 8;
|
||||||
|
|
||||||
|
} while (startCursor < PathTableSize + pad);
|
||||||
|
|
||||||
|
|
||||||
|
return pathNodes;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue