mirror of
https://github.com/SabreTools/SabreTools.Serialization.git
synced 2026-09-22 14:55:11 +00:00
ISO 9660 Extraction (#31)
* ISO9660 Extraction * Fix build * Decode strings in Printer * Double comment
This commit is contained in:
@@ -42,8 +42,9 @@ namespace SabreTools.Data.Models.ISO9660
|
||||
/// <summary>
|
||||
/// Map of sector numbers and the directory at that sector number
|
||||
/// Each Directory contains child directory and file descriptors
|
||||
/// Note: FileExtent is the base class for DirectoryExtent
|
||||
/// </summary>
|
||||
public Dictionary<int, DirectoryExtent> DirectoryDescriptors { get; set; }
|
||||
public Dictionary<int, FileExtent> DirectoryDescriptors { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -577,10 +577,10 @@ namespace SabreTools.Serialization.Readers
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="sectorLength">Number of bytes in a logical sector (usually 2048)</param>
|
||||
/// <param name="vd">Set of volume descriptors for a volume</param>
|
||||
/// <returns>Filled Dictionary of int to Directory on success, null on error</returns>
|
||||
public static Dictionary<int, DirectoryExtent>? ParseDirectoryDescriptors(Stream data, short sectorLength, VolumeDescriptor[] vdSet)
|
||||
/// <returns>Filled Dictionary of int to FileExtent on success, null on error</returns>
|
||||
public static Dictionary<int, FileExtent>? ParseDirectoryDescriptors(Stream data, short sectorLength, VolumeDescriptor[] vdSet)
|
||||
{
|
||||
var directories = new Dictionary<int, DirectoryExtent>();
|
||||
var directories = new Dictionary<int, FileExtent>();
|
||||
foreach (VolumeDescriptor vd in vdSet)
|
||||
{
|
||||
if (vd is not BaseVolumeDescriptor bvd)
|
||||
@@ -617,85 +617,104 @@ namespace SabreTools.Serialization.Readers
|
||||
/// <param name="blockLength">Number of bytes in a logical block (usually 2048)</param>
|
||||
/// <param name="dr">Directory record pointing to the directory extent</param>
|
||||
/// <param name="bigEndian">True if the Big Endian extent location/length should be parsed</param>
|
||||
/// <returns>Filled Dictionary of int to Directory on success, null on error</returns>
|
||||
public static Dictionary<int, DirectoryExtent>? ParseDirectory(Stream data, short sectorLength, short blockLength, DirectoryRecord dr, bool bigEndian)
|
||||
/// <returns>Filled Dictionary of int to FileExtent on success, null on error</returns>
|
||||
public static Dictionary<int, FileExtent>? ParseDirectory(Stream data, short sectorLength, short blockLength, DirectoryRecord dr, bool bigEndian)
|
||||
{
|
||||
// Do not parse file extents
|
||||
#if NET20 || NET35
|
||||
if ((dr.FileFlags & FileFlags.DIRECTORY) == 0)
|
||||
return null;
|
||||
#else
|
||||
if (!dr.FileFlags.HasFlag(FileFlags.DIRECTORY))
|
||||
return null;
|
||||
#endif
|
||||
|
||||
var directories = new Dictionary<int, FileExtent>();
|
||||
int blocksPerSector = sectorLength / blockLength;
|
||||
|
||||
// Validate both-endian extent location
|
||||
// TODO: Validate both-endian extent length (use the longest / non-zero one)
|
||||
int extentLocation = bigEndian ? dr.ExtentLocation.LittleEndian : dr.ExtentLocation.BigEndian;
|
||||
int extentLength = bigEndian ? dr.ExtentLength.LittleEndian : dr.ExtentLength.BigEndian;
|
||||
// Use provided extent endinanness
|
||||
int extentLocation = bigEndian ? dr.ExtentLocation.BigEndian : dr.ExtentLocation.LittleEndian;
|
||||
int extentLength = bigEndian ? dr.ExtentLength.BigEndian : dr.ExtentLength.LittleEndian;
|
||||
|
||||
// Validate extent within data stream
|
||||
if ((extentLocation * blockLength) + extentLength > data.Length)
|
||||
// Deal with extent length ambiguity
|
||||
if (!dr.ExtentLength.IsValid)
|
||||
{
|
||||
// If provided extent length is invalid, use the other value
|
||||
if (extentLength <= 0 || (extentLocation * blockLength) + extentLength > data.Length)
|
||||
extentLength = bigEndian ? dr.ExtentLength.LittleEndian : dr.ExtentLength.BigEndian;
|
||||
}
|
||||
|
||||
// Validate extent length
|
||||
if (extentLength <= 0 || (extentLocation * blockLength) + extentLength > data.Length)
|
||||
return null;
|
||||
|
||||
// Move stream to directory location
|
||||
data.SeekIfPossible(extentLocation * blockLength, SeekOrigin.Begin);
|
||||
|
||||
// Read all directory records in this directory
|
||||
var records = new List<DirectoryRecord>();
|
||||
int pos = 0;
|
||||
while (pos < extentLength)
|
||||
// Check if the current extent is a directory
|
||||
#if NET20 || NET35
|
||||
if ((dr.FileFlags & FileFlags.DIRECTORY) == FileFlags.DIRECTORY)
|
||||
#else
|
||||
if (dr.FileFlags.HasFlag(FileFlags.DIRECTORY))
|
||||
#endif
|
||||
{
|
||||
// Peek next byte to check whether the next record length is not greater than the end of the dir extent
|
||||
var recordLength = data.PeekByteValue();
|
||||
|
||||
// If record length of 0x00, next record begins in next sector
|
||||
if (recordLength == 0)
|
||||
// Read all directory records in this directory
|
||||
var records = new List<DirectoryRecord>();
|
||||
int pos = 0;
|
||||
while (pos < extentLength)
|
||||
{
|
||||
int paddingLength = sectorLength - (pos % sectorLength);
|
||||
pos += paddingLength;
|
||||
_ = data.ReadBytes(paddingLength);
|
||||
continue;
|
||||
// Peek next byte to check whether the next record length is not greater than the end of the dir extent
|
||||
var recordLength = data.PeekByteValue();
|
||||
|
||||
// If record length of 0x00, next record begins in next sector
|
||||
if (recordLength == 0)
|
||||
{
|
||||
int paddingLength = sectorLength - (pos % sectorLength);
|
||||
pos += paddingLength;
|
||||
_ = data.ReadBytes(paddingLength);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ensure record will end in this extent
|
||||
// TODO: Smartly detect record length for invalid record lengths
|
||||
pos += recordLength;
|
||||
if (pos > extentLength)
|
||||
break;
|
||||
|
||||
// Get the next directory record
|
||||
var directoryRecord = ParseDirectoryRecord(data, false);
|
||||
records.Add(directoryRecord);
|
||||
}
|
||||
|
||||
// Ensure record will end in this extent
|
||||
// TODO: Smartly detect record length for invalid record lengths
|
||||
pos += recordLength;
|
||||
if (pos > extentLength)
|
||||
break;
|
||||
// Add current directory to dictionary
|
||||
var currentDirectory = new DirectoryExtent();
|
||||
currentDirectory.DirectoryRecords = [.. records];
|
||||
directories.Add(extentLocation * blocksPerSector, currentDirectory);
|
||||
|
||||
// Get the next directory record
|
||||
var directoryRecord = ParseDirectoryRecord(data, false);
|
||||
records.Add(directoryRecord);
|
||||
// Add all child directories to dictionary recursively
|
||||
foreach (var record in records)
|
||||
{
|
||||
// Don't traverse to parent or self
|
||||
if (record.FileIdentifier.EqualsExactly(Constants.CurrentDirectory) || record.FileIdentifier.EqualsExactly(Constants.ParentDirectory))
|
||||
continue;
|
||||
|
||||
// Recursively parse child directory
|
||||
int sectorNum = record.ExtentLocation * blocksPerSector;
|
||||
var dir = ParseDirectory(data, sectorLength, blockLength, record, false);
|
||||
if (dir == null)
|
||||
continue;
|
||||
|
||||
// Add new directories to dictionary
|
||||
foreach (var kvp in dir)
|
||||
{
|
||||
if (!directories.ContainsKey(kvp.Key))
|
||||
directories.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add current directory to dictionary
|
||||
var directories = new Dictionary<int, DirectoryExtent>();
|
||||
var currentDirectory = new DirectoryExtent();
|
||||
currentDirectory.DirectoryRecords = [.. records];
|
||||
directories.Add(extentLocation * blocksPerSector, currentDirectory);
|
||||
|
||||
// Add all child directories to dictionary recursively
|
||||
foreach (var record in records)
|
||||
else
|
||||
{
|
||||
// Don't traverse to parent or self
|
||||
if (record.FileIdentifier.EqualsExactly(Constants.CurrentDirectory) || record.FileIdentifier.EqualsExactly(Constants.ParentDirectory))
|
||||
continue;
|
||||
|
||||
// Recursively parse child directory
|
||||
int sectorNum = record.ExtentLocation * blocksPerSector;
|
||||
var dir = ParseDirectory(data, sectorLength, blockLength, record, false);
|
||||
if (dir == null)
|
||||
continue;
|
||||
|
||||
// Add new directories to dictionary
|
||||
foreach (var kvp in dir)
|
||||
{
|
||||
if (!directories.ContainsKey(kvp.Key))
|
||||
directories.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
// 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);
|
||||
// }
|
||||
}
|
||||
|
||||
// If the extent location field is ambiguous, also parse the big-endian directory extent
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.Data.Models.ISO9660;
|
||||
using SabreTools.Data.Extensions;
|
||||
using SabreTools.IO;
|
||||
using SabreTools.IO.Extensions;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
@@ -9,15 +14,156 @@ namespace SabreTools.Serialization.Wrappers
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string outputDirectory, bool includeDebug)
|
||||
{
|
||||
// If we have no path tables or directory descriptors, there is nothing to extract
|
||||
if (PathTableGroups.Length == 0 && DirectoryDescriptors.Count == 0)
|
||||
// If we have no volume or directory descriptors, there is nothing to extract
|
||||
if (VolumeDescriptorSet.Length == 0 || DirectoryDescriptors.Count == 0)
|
||||
return true;
|
||||
|
||||
bool allExtracted = false;
|
||||
bool allExtracted = true;
|
||||
|
||||
// TODO: Extract all directories and file extents
|
||||
// Determine and validate sector length, default to 2048
|
||||
short sectorLength = (short)(SystemArea.Length / 16);
|
||||
if (sectorLength < 2048 || (sectorLength & (sectorLength - 1)) != 0)
|
||||
sectorLength = 2048;
|
||||
|
||||
// Keep track of extracted files according to their byte location
|
||||
// Note: Using Dictionary instead of HashSet because .NET Framework doesn't support HashSet
|
||||
var extractedFiles = new Dictionary<int, int>();
|
||||
|
||||
// Loop through all Base Volume Descriptors to extract files from each directory hierarchy
|
||||
// Note: This will prioritize the last volume descriptor directory hierarchies first (prioritises those filenames)
|
||||
for (int i = VolumeDescriptorSet.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (VolumeDescriptorSet[i] is BaseVolumeDescriptor bvd)
|
||||
{
|
||||
var rootDir = bvd.RootDirectoryRecord;
|
||||
|
||||
var blockLength = bvd.GetLogicalBlockSize(sectorLength);
|
||||
|
||||
// TODO: Better encoding detection (EscapeSequences)
|
||||
var encoding = Encoding.UTF8;
|
||||
if (bvd is SupplementaryVolumeDescriptor svd)
|
||||
encoding = Encoding.BigEndianUnicode;
|
||||
|
||||
// Extract all files within root directory hierarchy
|
||||
allExtracted &= ExtractExtent(rootDir.ExtentLocation.LittleEndian, extractedFiles, encoding, blockLength, outputDirectory, includeDebug);
|
||||
// If Big Endian extent location differs from Little Endian extent location, also extract that directory hierarchy
|
||||
if (!rootDir.ExtentLocation.IsValid)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine($"Extracting from volume descriptor (big endian root dir location)");
|
||||
allExtracted &= ExtractExtent(rootDir.ExtentLocation.BigEndian, extractedFiles, encoding, blockLength, outputDirectory, includeDebug);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allExtracted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract all files from within a directory/file extent
|
||||
/// </summary>
|
||||
private bool ExtractExtent(int extentLocation, Dictionary<int, int> extractedFiles, Encoding encoding, int blockLength, string outputDirectory, bool includeDebug)
|
||||
{
|
||||
// Check that directory exists in model
|
||||
if (!DirectoryDescriptors.ContainsKey(extentLocation))
|
||||
return false;
|
||||
|
||||
bool succeeded = true;
|
||||
if (DirectoryDescriptors[extentLocation] is DirectoryExtent dir)
|
||||
{
|
||||
foreach (var dr in dir.DirectoryRecords)
|
||||
{
|
||||
// Recurse if record is directory
|
||||
if ((dr.FileFlags & FileFlags.DIRECTORY) == FileFlags.DIRECTORY)
|
||||
{
|
||||
// Don't recurse up or self
|
||||
if (dr.FileIdentifier.EqualsExactly(Constants.CurrentDirectory) || dr.FileIdentifier.EqualsExactly(Constants.ParentDirectory))
|
||||
continue;
|
||||
|
||||
// Append directory name
|
||||
string outDirTemp = Path.Combine(outputDirectory, encoding.GetString(dr.FileIdentifier));
|
||||
if (includeDebug) Console.WriteLine($"Extracting to directory: {outDirTemp}");
|
||||
ExtractExtent(dr.ExtentLocation.LittleEndian, extractedFiles, encoding, blockLength, outDirTemp, includeDebug);
|
||||
|
||||
// Also extract from BigEndian values if ambiguous
|
||||
if (!dr.ExtentLocation.IsValid!)
|
||||
{
|
||||
ExtractExtent(dr.ExtentLocation.BigEndian, extractedFiles, encoding, blockLength, outDirTemp, includeDebug);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Record is a file extent, extract file
|
||||
succeeded &= ExtractFile(dr, extractedFiles, encoding, blockLength, false, outputDirectory, includeDebug);
|
||||
// Also extract from BigEndian values if ambiguous
|
||||
if (!dr.ExtentLocation.IsValid!)
|
||||
{
|
||||
succeeded &= ExtractFile(dr, extractedFiles, encoding, blockLength, true, outputDirectory, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract file pointed to by a directory record
|
||||
/// </summary>
|
||||
private bool ExtractFile(DirectoryRecord dr, Dictionary<int, int> extractedFiles, Encoding encoding, int blockLength, bool bigEndian, string outputDirectory, bool includeDebug)
|
||||
{
|
||||
// Cannot extract file if it is a directory
|
||||
if ((dr.FileFlags & FileFlags.DIRECTORY) == FileFlags.DIRECTORY)
|
||||
return false;
|
||||
|
||||
int extentLocation = bigEndian ? dr.ExtentLocation.BigEndian : dr.ExtentLocation.LittleEndian;
|
||||
int fileOffset = dr.ExtentLocation * blockLength;
|
||||
|
||||
// Check that the file hasn't been extracted already
|
||||
if (extractedFiles.ContainsKey(fileOffset))
|
||||
return true;
|
||||
|
||||
const int chunkSize = 2048 * 1024;
|
||||
lock (_dataSourceLock)
|
||||
{
|
||||
_dataSource.SeekIfPossible(fileOffset, SeekOrigin.Begin);
|
||||
|
||||
// Get the length, and make sure it won't EOF
|
||||
int length = dr.ExtentLength;
|
||||
if (length > _dataSource.Length - _dataSource.Position)
|
||||
return false;
|
||||
|
||||
// TODO: Decode properly (Use VD's separator characters and encoding)
|
||||
string filename = encoding.GetString(dr.FileIdentifier);
|
||||
int index = filename.IndexOf(';');
|
||||
if (index > 0)
|
||||
filename = filename.Substring(0, index);
|
||||
|
||||
// Ensure the full output directory exists
|
||||
filename = Path.Combine(outputDirectory, filename);
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null && !Directory.Exists(directoryName))
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Write the output file
|
||||
if (includeDebug) Console.WriteLine($"Extracting: {filename}");
|
||||
using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
|
||||
while (length > 0)
|
||||
{
|
||||
int bytesToRead = (int)Math.Min(length, chunkSize);
|
||||
|
||||
byte[] buffer = _dataSource.ReadBytes(bytesToRead);
|
||||
fs.Write(buffer, 0, bytesToRead);
|
||||
fs.Flush();
|
||||
|
||||
length -= bytesToRead;
|
||||
}
|
||||
|
||||
// Mark the file as extracted
|
||||
extractedFiles.Add(fileOffset, dr.ExtentLength);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,11 @@ namespace SabreTools.Serialization.Wrappers
|
||||
builder.AppendLine();
|
||||
|
||||
Print(builder, Model.VolumeDescriptorSet);
|
||||
Print(builder, Model.PathTableGroups);
|
||||
Print(builder, Model.DirectoryDescriptors);
|
||||
|
||||
// TODO: Parse the volume descriptors to print the Path Table Groups and Directory Descriptors with proper encoding
|
||||
Encoding encoding = Encoding.UTF8;
|
||||
Print(builder, Model.PathTableGroups, encoding);
|
||||
Print(builder, Model.DirectoryDescriptors, encoding);
|
||||
}
|
||||
|
||||
#region Volume Descriptors
|
||||
@@ -96,12 +99,16 @@ namespace SabreTools.Serialization.Wrappers
|
||||
builder.AppendLine(" Unidentified Base Volume Descriptor:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
|
||||
// Default to UTF-8 deocding (note: Spec says PVD uses subset of ASCII, but UTF-8 is used here as it is a strict superset)
|
||||
Encoding encoding = Encoding.UTF8;
|
||||
if (vd is PrimaryVolumeDescriptor pvd)
|
||||
{
|
||||
builder.AppendLine(pvd.UnusedByte, " Unused Byte");
|
||||
}
|
||||
else if (vd is SupplementaryVolumeDescriptor svd)
|
||||
{
|
||||
// Decode strings using UTF-16 BigEndian (note: Spec says SVD uses UCS-2, but UTF-16 is used here as it is a strict superset)
|
||||
encoding = Encoding.BigEndianUnicode;
|
||||
builder.AppendLine(" Volume Flags:");
|
||||
#if NET20 || NET35
|
||||
builder.AppendLine((svd.VolumeFlags & VolumeFlags.UNREGISTERED_ESCAPE_SEQUENCES) != 0, " Unregistered Escape Sequences");
|
||||
@@ -114,10 +121,10 @@ namespace SabreTools.Serialization.Wrappers
|
||||
builder.AppendLine("Zeroed", " Reserved Flags");
|
||||
}
|
||||
|
||||
// TODO: Decode all byte arrays into strings (based on encoding above)
|
||||
// TODO: Better string decoding (based on spec, EscapeSequences, and detection)
|
||||
|
||||
builder.AppendLine(vd.SystemIdentifier, " System Identifier");
|
||||
builder.AppendLine(vd.VolumeIdentifier, " Volume Identifier");
|
||||
builder.AppendLine(encoding.GetString(vd.SystemIdentifier), " System Identifier");
|
||||
builder.AppendLine(encoding.GetString(vd.VolumeIdentifier), " Volume Identifier");
|
||||
|
||||
|
||||
if (vd.Unused8Bytes != null && Array.TrueForAll(vd.Unused8Bytes, b => b == 0))
|
||||
@@ -150,15 +157,15 @@ namespace SabreTools.Serialization.Wrappers
|
||||
builder.AppendLine(vd.OptionalPathTableLocationM, " Optional Type-M Path Table Location");
|
||||
|
||||
builder.AppendLine(" Root Directory Record:");
|
||||
Print(builder, vd.RootDirectoryRecord);
|
||||
Print(builder, vd.RootDirectoryRecord, encoding);
|
||||
|
||||
builder.AppendLine(vd.VolumeSetIdentifier, " Volume Set Identifier");
|
||||
builder.AppendLine(vd.PublisherIdentifier, " Publisher Identifier");
|
||||
builder.AppendLine(vd.DataPreparerIdentifier, " Data Preparer Identifier");
|
||||
builder.AppendLine(vd.ApplicationIdentifier, " Application Identifier");
|
||||
builder.AppendLine(vd.CopyrightFileIdentifier, " Copyright Identifier");
|
||||
builder.AppendLine(vd.AbstractFileIdentifier, " Abstract Identifier");
|
||||
builder.AppendLine(vd.BibliographicFileIdentifier, " Bibliographic Identifier");
|
||||
builder.AppendLine(encoding.GetString(vd.VolumeSetIdentifier), " Volume Set Identifier");
|
||||
builder.AppendLine(encoding.GetString(vd.PublisherIdentifier), " Publisher Identifier");
|
||||
builder.AppendLine(encoding.GetString(vd.DataPreparerIdentifier), " Data Preparer Identifier");
|
||||
builder.AppendLine(encoding.GetString(vd.ApplicationIdentifier), " Application Identifier");
|
||||
builder.AppendLine(encoding.GetString(vd.CopyrightFileIdentifier), " Copyright Identifier");
|
||||
builder.AppendLine(encoding.GetString(vd.AbstractFileIdentifier), " Abstract Identifier");
|
||||
builder.AppendLine(encoding.GetString(vd.BibliographicFileIdentifier), " Bibliographic Identifier");
|
||||
|
||||
builder.AppendLine(Format(vd.VolumeCreationDateTime), " Volume Creation Date Time:");
|
||||
builder.AppendLine(Format(vd.VolumeModificationDateTime), " Volume Modification Date Time:");
|
||||
@@ -240,7 +247,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#region Path Tables
|
||||
|
||||
private static void Print(StringBuilder builder, PathTableGroup[]? ptgs)
|
||||
private static void Print(StringBuilder builder, PathTableGroup[]? ptgs, Encoding encoding)
|
||||
{
|
||||
builder.AppendLine(" Path Table Group(s):");
|
||||
builder.AppendLine(" -------------------------");
|
||||
@@ -257,7 +264,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
builder.AppendLine($" Type-L Path Table {tableNum}:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
Print(builder, ptgs[tableNum].PathTableL);
|
||||
Print(builder, ptgs[tableNum].PathTableL, encoding);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -268,7 +275,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
builder.AppendLine($" Optional Type-L Path Table {tableNum}:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
Print(builder, ptgs[tableNum].OptionalPathTableL);
|
||||
Print(builder, ptgs[tableNum].OptionalPathTableL, encoding);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -279,7 +286,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
builder.AppendLine($" Type-M Path Table {tableNum}:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
Print(builder, ptgs[tableNum].PathTableM);
|
||||
Print(builder, ptgs[tableNum].PathTableM, encoding);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -290,7 +297,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
builder.AppendLine($" Optional Type-M Path Table {tableNum}:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
Print(builder, ptgs[tableNum].OptionalPathTableM);
|
||||
Print(builder, ptgs[tableNum].OptionalPathTableM, encoding);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -302,7 +309,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, PathTableRecord[] records)
|
||||
private static void Print(StringBuilder builder, PathTableRecord[] records, Encoding encoding)
|
||||
{
|
||||
if (records.Length == 0)
|
||||
{
|
||||
@@ -329,7 +336,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#region Directories
|
||||
|
||||
private static void Print(StringBuilder builder, Dictionary<int, DirectoryExtent>? dirs)
|
||||
private static void Print(StringBuilder builder, Dictionary<int, FileExtent>? dirs, Encoding encoding)
|
||||
{
|
||||
builder.AppendLine(" Directory Descriptors Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
@@ -344,39 +351,56 @@ namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
builder.AppendLine($" Directory at Sector {kvp.Key}");
|
||||
builder.AppendLine(" -------------------------");
|
||||
Print(builder, kvp.Value);
|
||||
Print(builder, kvp.Value, encoding);
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, DirectoryExtent? dir)
|
||||
private static void Print(StringBuilder builder, FileExtent? extent, Encoding encoding)
|
||||
{
|
||||
if (dir == null)
|
||||
if (extent == null)
|
||||
{
|
||||
builder.AppendLine(" No directory descriptor");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
if (dir.DirectoryRecords == null)
|
||||
{
|
||||
builder.AppendLine(" No directory records");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int recordNum = 0; recordNum < dir.DirectoryRecords.Length; recordNum++)
|
||||
if (extent is DirectoryExtent dir)
|
||||
{
|
||||
builder.AppendLine($" Directory Record {recordNum}:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
Print(builder, dir.DirectoryRecords[recordNum]);
|
||||
builder.AppendLine();
|
||||
if (dir.DirectoryRecords == null)
|
||||
{
|
||||
builder.AppendLine(" No directory records");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
// File extent is a directory, print all directory records
|
||||
for (int recordNum = 0; recordNum < dir.DirectoryRecords.Length; recordNum++)
|
||||
{
|
||||
builder.AppendLine($" Directory Record {recordNum}:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
Print(builder, dir.DirectoryRecords[recordNum], encoding);
|
||||
builder.AppendLine();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// File extent is a file, print the file's Extended Attribute Record
|
||||
Print(builder, extent.ExtendedAttributeRecord);
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, DirectoryRecord? dr)
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -15,6 +15,9 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#region Extension Properties
|
||||
|
||||
/// <inheritdoc cref="Volume.SystemArea"/>
|
||||
public byte[] SystemArea => Model.SystemArea ?? [];
|
||||
|
||||
/// <inheritdoc cref="Volume.VolumeDescriptorSet"/>
|
||||
public VolumeDescriptor[] VolumeDescriptorSet => Model.VolumeDescriptorSet ?? [];
|
||||
|
||||
@@ -22,7 +25,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
public PathTableGroup[] PathTableGroups => Model.PathTableGroups ?? [];
|
||||
|
||||
/// <inheritdoc cref="Volume.DirectoryDescriptors"/>
|
||||
public Dictionary<int, DirectoryExtent> DirectoryDescriptors => Model.DirectoryDescriptors ?? [];
|
||||
public Dictionary<int, FileExtent> DirectoryDescriptors => Model.DirectoryDescriptors ?? [];
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
#region Data
|
||||
|
||||
/// <summary>
|
||||
/// Read a number of bytes from an offset fomr the data source, if possible
|
||||
/// Read a number of bytes from an offset from the data source, if possible
|
||||
/// </summary>
|
||||
/// <param name="offset">Offset within the data source to start reading</param>
|
||||
/// <param name="length">Number of bytes to read from the offset</param>
|
||||
|
||||
Reference in New Issue
Block a user