mirror of
https://github.com/SabreTools/SabreTools.Serialization.git
synced 2026-02-04 05:36:12 +00:00
Support ISO9660 Extended Attribute Record (#34)
* Support ISO9660 Extended Attribute Record * fix * Fix * Don't extract multi-extent files * ReadUInt16LittleEndian * debug * Fix * Skip EAR when extracting files * Add comment about EAR * Fix same-name extraction * Safer directory parsing * even safer * return null * more directory record checks * not nullable nullable * init system use * valid records should pass * review * remove empty case
This commit is contained in:
@@ -14,18 +14,19 @@ namespace SabreTools.Data.Models.ISO9660
|
||||
public byte DirectoryRecordLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Length of the extended attribute record
|
||||
/// Length of the extended attribute record (in number of logical blocks)
|
||||
/// If no extended attribute record is used, set to 0x00
|
||||
/// </summary>
|
||||
public byte ExtendedAttributeRecordLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Logical block number of the first logical block allocated to this extent
|
||||
/// Extent begins with the extended attribute record (if present)
|
||||
/// </summary>
|
||||
public BothInt32 ExtentLocation { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Number of bytes allocated to this extent
|
||||
/// Number of bytes allocated to this extent (not including extended attribute record length)
|
||||
/// </summary>
|
||||
public BothInt32 ExtentLength { get; set; } = 0;
|
||||
|
||||
|
||||
@@ -649,6 +649,11 @@ namespace SabreTools.Serialization.Readers
|
||||
if (dr.FileFlags.HasFlag(FileFlags.DIRECTORY))
|
||||
#endif
|
||||
{
|
||||
// Start of directory should not be 0
|
||||
int firstRecordLength = data.PeekByteValue();
|
||||
if (firstRecordLength == 0)
|
||||
return null;
|
||||
|
||||
// Read all directory records in this directory
|
||||
var records = new List<DirectoryRecord>();
|
||||
int pos = 0;
|
||||
@@ -663,6 +668,16 @@ namespace SabreTools.Serialization.Readers
|
||||
int paddingLength = sectorLength - (pos % sectorLength);
|
||||
pos += paddingLength;
|
||||
_ = data.ReadBytes(paddingLength);
|
||||
|
||||
// Finish parsing records if end reached
|
||||
if (pos >= extentLength)
|
||||
break;
|
||||
|
||||
// Start of sector should not be 0, ignore entire directory
|
||||
int nextRecordLength = data.PeekByteValue();
|
||||
if (nextRecordLength <= paddingLength)
|
||||
return null;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -674,6 +689,12 @@ namespace SabreTools.Serialization.Readers
|
||||
|
||||
// Get the next directory record
|
||||
var directoryRecord = ParseDirectoryRecord(data, false);
|
||||
|
||||
// Compare recordLength with number of bytes in directoryRecord and return null if mismatch
|
||||
var readLength = 33 + directoryRecord.FileIdentifier.Length + (directoryRecord.PaddingField == null ? 0 : 1) + directoryRecord.SystemUse.Length;
|
||||
if (readLength != recordLength)
|
||||
return null;
|
||||
|
||||
records.Add(directoryRecord);
|
||||
}
|
||||
|
||||
@@ -705,20 +726,25 @@ namespace SabreTools.Serialization.Readers
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Create ParseExtendedAttributeRecord()
|
||||
// Extent is a file, parse the Extended Attribute Record
|
||||
// var ear = ParseExtendedAttributeRecord();
|
||||
// if (ear != null)
|
||||
// {
|
||||
// var fileExtent = new FileExtent();
|
||||
// fileExtent.ExtendedAttributeRecord = ear;
|
||||
// if (!directories.ContainsKey(extentLocation))
|
||||
// directories.Add(extentLocation, fileExtent);
|
||||
// }
|
||||
var fileExtent = new FileExtent();
|
||||
if (dr.ExtendedAttributeRecordLength > 0)
|
||||
{
|
||||
var ear = ParseExtendedAttributeRecord(data);
|
||||
if (ear != null)
|
||||
{
|
||||
fileExtent.ExtendedAttributeRecord = ear;
|
||||
}
|
||||
// Do not parse file data into file extent, too large
|
||||
|
||||
// Put the file extent is the dictionary
|
||||
if (!directories.ContainsKey(extentLocation))
|
||||
directories.Add(extentLocation, fileExtent);
|
||||
}
|
||||
}
|
||||
|
||||
// If the extent location field is ambiguous, also parse the big-endian directory extent
|
||||
if (!dr.ExtentLocation.IsValid)
|
||||
if (!bigEndian && dr.ExtentLocation.IsValid)
|
||||
{
|
||||
var bigEndianDir = ParseDirectory(data, sectorLength, blockLength, dr, true);
|
||||
if (bigEndianDir != null)
|
||||
@@ -806,6 +832,42 @@ namespace SabreTools.Serialization.Readers
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ExtendedAttributeRecord
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="root">true if root directory record, false otherwise</param>
|
||||
/// <returns>Filled ExtendedAttributeRecord on success, null on error</returns>
|
||||
public static ExtendedAttributeRecord ParseExtendedAttributeRecord(Stream data)
|
||||
{
|
||||
var obj = new ExtendedAttributeRecord();
|
||||
|
||||
obj.OwnerIdentification = data.ReadInt16BothEndian();
|
||||
obj.GroupIdentification = data.ReadInt16BothEndian();
|
||||
obj.Permissions = (Permissions)data.ReadUInt16LittleEndian();
|
||||
obj.FileCreationDateTime = ParseDecDateTime(data);
|
||||
obj.FileModificationDateTime = ParseDecDateTime(data);
|
||||
obj.FileExpirationDateTime = ParseDecDateTime(data);
|
||||
obj.FileEffectiveDateTime = ParseDecDateTime(data);
|
||||
obj.RecordFormat = (RecordFormat)data.ReadByteValue();
|
||||
obj.RecordAttributes = (RecordAttributes)data.ReadByteValue();
|
||||
obj.RecordLength = data.ReadInt16BothEndian();
|
||||
obj.SystemIdentifier = data.ReadBytes(32);
|
||||
obj.SystemUse = data.ReadBytes(64);
|
||||
obj.ExtendedAttributeRecordVersion = data.ReadByteValue();
|
||||
obj.EscapeSequencesLength = data.ReadByteValue();
|
||||
obj.Reserved64Bytes = data.ReadBytes(64);
|
||||
obj.ApplicationLength = data.ReadInt16BothEndian();
|
||||
|
||||
if (obj.ApplicationLength > 0)
|
||||
obj.ApplicationUse = data.ReadBytes(obj.ApplicationLength);
|
||||
|
||||
if (obj.EscapeSequencesLength > 0)
|
||||
obj.EscapeSequences = data.ReadBytes(obj.EscapeSequencesLength);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a DirectoryRecordDateTime
|
||||
/// </summary>
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
ExtractExtent(dr.ExtentLocation.BigEndian, extractedFiles, encoding, blockLength, outDirTemp, includeDebug);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if ((dr.FileFlags & FileFlags.MULTI_EXTENT) == 0)
|
||||
{
|
||||
// Record is a file extent, extract file
|
||||
succeeded &= ExtractFile(dr, extractedFiles, encoding, blockLength, false, outputDirectory, includeDebug);
|
||||
@@ -99,7 +99,10 @@ namespace SabreTools.Serialization.Wrappers
|
||||
succeeded &= ExtractFile(dr, extractedFiles, encoding, blockLength, true, outputDirectory, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (includeDebug) Console.WriteLine("Extraction of multi-extent files is currently not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +119,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
return false;
|
||||
|
||||
int extentLocation = bigEndian ? dr.ExtentLocation.BigEndian : dr.ExtentLocation.LittleEndian;
|
||||
int fileOffset = dr.ExtentLocation * blockLength;
|
||||
int fileOffset = (dr.ExtentLocation + dr.ExtendedAttributeRecordLength) * blockLength;
|
||||
|
||||
// Check that the file hasn't been extracted already
|
||||
if (extractedFiles.ContainsKey(fileOffset))
|
||||
@@ -134,7 +137,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
// TODO: Decode properly (Use VD's separator characters and encoding)
|
||||
string filename = encoding.GetString(dr.FileIdentifier);
|
||||
int index = filename.IndexOf(';');
|
||||
int index = filename.LastIndexOf(';');
|
||||
if (index > 0)
|
||||
filename = filename.Substring(0, index);
|
||||
|
||||
@@ -143,6 +146,13 @@ namespace SabreTools.Serialization.Wrappers
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null && !Directory.Exists(directoryName))
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Check that the output file doesn't already exist
|
||||
if (File.Exists(filename) || Directory.Exists(filename))
|
||||
{
|
||||
if (includeDebug) Console.WriteLine($"File/Folder already exists, cannot extract: {filename}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write the output file
|
||||
if (includeDebug) Console.WriteLine($"Extracting: {filename}");
|
||||
|
||||
@@ -353,8 +353,6 @@ namespace SabreTools.Serialization.Wrappers
|
||||
builder.AppendLine(" -------------------------");
|
||||
Print(builder, kvp.Value, encoding);
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, FileExtent? extent, Encoding encoding)
|
||||
@@ -387,19 +385,12 @@ namespace SabreTools.Serialization.Wrappers
|
||||
else
|
||||
{
|
||||
// File extent is a file, print the file's Extended Attribute Record
|
||||
Print(builder, extent.ExtendedAttributeRecord);
|
||||
Print(builder, extent.ExtendedAttributeRecord, encoding);
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, ExtendedAttributeRecord? ear)
|
||||
{
|
||||
// TODO: Implement ExtendedAttributeRecord printing
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, DirectoryRecord? dr, Encoding encoding)
|
||||
{
|
||||
if (dr == null)
|
||||
@@ -463,6 +454,48 @@ namespace SabreTools.Serialization.Wrappers
|
||||
builder.AppendLine(tz, " Timezone Offset");
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, ExtendedAttributeRecord? ear, Encoding encoding)
|
||||
{
|
||||
builder.AppendLine(" File Extent");
|
||||
if (ear == null)
|
||||
return;
|
||||
|
||||
builder.AppendLineBothEndian(ear.OwnerIdentification, " Owner Identification");
|
||||
builder.AppendLineBothEndian(ear.OwnerIdentification, " Group Identification");
|
||||
|
||||
builder.AppendLine(" Permissions:");
|
||||
builder.AppendLine((ear.Permissions & Permissions.SYSTEM_USER_CANNOT_READ) != 0, " System User Cannot Read");
|
||||
builder.AppendLine((ear.Permissions & Permissions.SYSTEM_USER_CANNOT_EXECUTE) != 0, " System User Cannot Execute");
|
||||
builder.AppendLine((ear.Permissions & Permissions.OWNER_CANNOT_READ) != 0, " System User Cannot Execute");
|
||||
builder.AppendLine((ear.Permissions & Permissions.OWNER_CANNOT_EXECUTE) != 0, " System User Cannot Execute");
|
||||
builder.AppendLine((ear.Permissions & Permissions.GROUP_MEMBER_CANNOT_READ) != 0, " System User Cannot Execute");
|
||||
builder.AppendLine((ear.Permissions & Permissions.GROUP_MEMBER_CANNOT_EXECUTE) != 0, " System User Cannot Execute");
|
||||
builder.AppendLine((ear.Permissions & Permissions.NON_GROUP_MEMBER_CANNOT_READ) != 0, " System User Cannot Execute");
|
||||
builder.AppendLine((ear.Permissions & Permissions.NON_GROUP_MEMBER_CANNOT_EXECUTE) != 0, " System User Cannot Execute");
|
||||
if ((ear.Permissions & Permissions.PERMISSIONS_MASK) == Permissions.PERMISSIONS_MASK)
|
||||
builder.AppendLine(" Fixed Bits: All Set");
|
||||
else
|
||||
builder.AppendLine(" Fixed Bits: Not All Set");
|
||||
|
||||
|
||||
builder.AppendLine(Format(ear.FileCreationDateTime), " File Creation Date Time");
|
||||
builder.AppendLine(Format(ear.FileModificationDateTime), " File Modification Date Time");
|
||||
builder.AppendLine(Format(ear.FileExpirationDateTime), " File Expiration Date Time");
|
||||
builder.AppendLine(Format(ear.FileEffectiveDateTime), " File Effective Date Time");
|
||||
|
||||
builder.AppendLine((byte)ear.RecordFormat, " Record Format:");
|
||||
builder.AppendLine((byte)ear.RecordAttributes, " Record Attributes");
|
||||
builder.AppendLineBothEndian(ear.RecordLength, " Record Length");
|
||||
builder.AppendLine(encoding.GetString(ear.SystemIdentifier), " System Identifier");
|
||||
builder.AppendLine(ear.SystemUse, " System Use");
|
||||
builder.AppendLine(ear.ExtendedAttributeRecordVersion, " Extended Attribute Record Version");
|
||||
builder.AppendLine(ear.EscapeSequencesLength, " Escape Sequences Length");
|
||||
builder.AppendLine(ear.Reserved64Bytes, " Reserved 64 Bytes");
|
||||
builder.AppendLineBothEndian(ear.ApplicationLength, " Application Length");
|
||||
builder.AppendLine(ear.ApplicationUse, " Application Use");
|
||||
builder.AppendLine(ear.EscapeSequences, " Escape Sequences");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static string? Format(DecDateTime? dt)
|
||||
|
||||
Reference in New Issue
Block a user