BizHawk/ExternalProjects/iso-parser/ISONodeRecord.cs

301 lines
8.0 KiB
C#

using System.Text;
using System.IO;
namespace ISOParser
{
/// <summary>
/// Class to represent the file/directory information read from the disk.
/// </summary>
public class ISONodeRecord
{
/// <summary>
/// String representing the current directory entry
/// </summary>
public const string CURRENT_DIRECTORY = ".";
/// <summary>
/// String representing the parent directory entry
/// </summary>
public const string PARENT_DIRECTORY = "..";
/// <summary>
/// The length of the record in bytes.
/// </summary>
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>
/// The file offset of the data for this file/directory (in sectors).
/// </summary>
public long OffsetOfData;
/// <summary>
/// The length of the data for this file/directory (in bytes).
/// </summary>
public long LengthOfData;
/// <summary>
/// The file/directory creation year since 1900.
/// </summary>
public byte Year;
/// <summary>
/// The file/directory creation month.
/// </summary>
public byte Month;
/// <summary>
/// The file/directory creation day.
/// </summary>
public byte Day;
/// <summary>
/// The file/directory creation hour.
/// </summary>
public byte Hour;
/// <summary>
/// The file/directory creation minute.
/// </summary>
public byte Minute;
/// <summary>
/// The file/directory creation second.
/// </summary>
public byte Second;
/// <summary>
/// The file time offset from GMT.
/// </summary>
public byte TimeZoneOffset;
/// <summary>
/// Flags representing the attributes of this file/directory.
/// </summary>
public byte Flags;
/// <summary>
/// The length of the file/directory name.
/// </summary>
public byte NameLength;
/// <summary>
/// The file/directory name.
/// </summary>
public string Name;
/// <summary>
/// Constructor
/// </summary>
public ISONodeRecord()
{
// Set initial values
this.Length = 0;
this.OffsetOfData = 0;
this.LengthOfData = 0;
this.Year = 0;
this.Month = 0;
this.Day = 0;
this.Hour = 0;
this.Minute = 0;
this.Second = 0;
this.TimeZoneOffset = 0;
this.Flags = 0;
this.NameLength = 0;
this.Name = null;
}
/// <summary>
/// Return true if the record represents a file.
/// </summary>
/// <returns>True if a file.</returns>
public bool IsFile()
{
return ((this.Flags >> 1) & 0x01) == 0;
}
/// <summary>
/// Return true if the record represents a directory.
/// </summary>
/// <returns>True if a directory.</returns>
public bool IsDirectory()
{
return ((this.Flags >> 1) & 0x01) == 1;
}
/// <summary>
/// Parse the record from an array and offset.
/// </summary>
/// <param name="data">The array to parse from.</param>
/// <param name="cursor">The offset to start parsing at.</param>
public void Parse(byte[] data, int cursor)
{
// Put the array into a memory stream and pass to the main parsing function
MemoryStream s = new MemoryStream(data);
s.Seek(cursor, SeekOrigin.Begin);
if (ISOFile.Format == ISOFile.ISOFormat.ISO9660)
this.ParseISO9660(s);
if (ISOFile.Format == ISOFile.ISOFormat.CDInteractive)
this.ParseCDInteractive(s);
}
/// <summary>
/// Parse the node record from the given ISO9660 stream.
/// </summary>
/// <param name="s">The stream to parse from.</param>
public void ParseISO9660(Stream s)
{
EndianBitConverter bc = EndianBitConverter.CreateForLittleEndian();
long startPosition = s.Position;
byte[] buffer = new byte[ISOFile.SECTOR_SIZE];
// Get the length
s.Read(buffer, 0, 1);
this.Length = buffer[0];
//the number of sectors in the attribute record
s.Read(buffer, 0, 1);
// Read Data Offset
s.Read(buffer, 0, 8);
this.OffsetOfData = bc.ToInt32(buffer);
// Read Data Length
s.Read(buffer, 0, 8);
this.LengthOfData = bc.ToInt32(buffer);
// Read the time and flags
s.Read(buffer, 0, 8);
this.Year = buffer[0];
this.Month = buffer[1];
this.Day = buffer[2];
this.Hour = buffer[3];
this.Minute = buffer[4];
this.Second = buffer[5];
this.TimeZoneOffset = buffer[6];
this.Flags = buffer[7];
s.Read(buffer, 0, 6);
// Read the name length
s.Read(buffer, 0, 1);
this.NameLength = buffer[0];
// Read the directory name
s.Read(buffer, 0, this.NameLength);
if (this.NameLength == 1 && (buffer[0] == 0 || buffer[0] == 1))
{
if (buffer[0] == 0)
this.Name = ISONodeRecord.CURRENT_DIRECTORY;
else
this.Name = ISONodeRecord.PARENT_DIRECTORY;
}
else
{
this.Name = ASCIIEncoding.ASCII.GetString(buffer, 0, this.NameLength);
}
// Seek to end
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);
}
}
}