diff --git a/SabreTools.Serialization/Readers/ISO9660.cs b/SabreTools.Serialization/Readers/ISO9660.cs
index 3b7487d6..0a42df4a 100644
--- a/SabreTools.Serialization/Readers/ISO9660.cs
+++ b/SabreTools.Serialization/Readers/ISO9660.cs
@@ -626,21 +626,23 @@ namespace SabreTools.Serialization.Readers
// Use provided extent endinanness
int extentLocation = bigEndian ? dr.ExtentLocation.BigEndian : dr.ExtentLocation.LittleEndian;
int extentLength = bigEndian ? dr.ExtentLength.BigEndian : dr.ExtentLength.LittleEndian;
+ long extentOffset = (long)extentLocation * (long)blockLength;
+ long extentFinal = extentOffset + (long)extentLength;
// 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)
+ if (extentLength <= 0 || extentFinal > (long)data.Length)
extentLength = bigEndian ? dr.ExtentLength.LittleEndian : dr.ExtentLength.BigEndian;
}
// Validate extent length
- if (extentLength <= 0 || (extentLocation * blockLength) + extentLength > data.Length)
+ if (extentLength <= 0 || extentFinal > (long)data.Length)
return null;
// Move stream to directory location
- data.SeekIfPossible(extentLocation * blockLength, SeekOrigin.Begin);
+ data.SeekIfPossible(extentOffset, SeekOrigin.Begin);
// Check if the current extent is a directory
#if NET20 || NET35
@@ -701,7 +703,7 @@ namespace SabreTools.Serialization.Readers
// Add current directory to dictionary
var currentDirectory = new DirectoryExtent();
currentDirectory.DirectoryRecords = [.. records];
- directories.Add(extentLocation * blocksPerSector, currentDirectory);
+ directories.Add(extentLocation, currentDirectory);
// Add all child directories to dictionary recursively
foreach (var record in records)
diff --git a/SabreTools.Serialization/Wrappers/ISO9660.Extraction.cs b/SabreTools.Serialization/Wrappers/ISO9660.Extraction.cs
index 0a1a3476..858b885a 100644
--- a/SabreTools.Serialization/Wrappers/ISO9660.Extraction.cs
+++ b/SabreTools.Serialization/Wrappers/ISO9660.Extraction.cs
@@ -10,6 +10,21 @@ namespace SabreTools.Serialization.Wrappers
{
public partial class ISO9660 : IExtractable
{
+ #region Extraction State
+
+ ///
+ /// List of extracted files by their sector offset
+ ///
+ private Dictionary extractedFiles = [];
+
+ ///
+ /// List of multi-extent files written, by their FileIdentifier
+ ///
+ /// Used as last-extent has mutli-extent flag=0
+ private List multiExtentFiles = [];
+
+ #endregion
+
///
public virtual bool Extract(string outputDirectory, bool includeDebug)
{
@@ -24,10 +39,6 @@ namespace SabreTools.Serialization.Wrappers
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();
-
// 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--)
@@ -44,12 +55,12 @@ namespace SabreTools.Serialization.Wrappers
encoding = Encoding.BigEndianUnicode;
// Extract all files within root directory hierarchy
- allExtracted &= ExtractExtent(rootDir.ExtentLocation.LittleEndian, extractedFiles, encoding, blockLength, outputDirectory, includeDebug);
+ allExtracted &= ExtractExtent(rootDir.ExtentLocation.LittleEndian, 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);
+ allExtracted &= ExtractExtent(rootDir.ExtentLocation.BigEndian, encoding, blockLength, outputDirectory, includeDebug);
}
}
}
@@ -60,7 +71,7 @@ namespace SabreTools.Serialization.Wrappers
///
/// Extract all files from within a directory/file extent
///
- private bool ExtractExtent(int extentLocation, Dictionary extractedFiles, Encoding encoding, int blockLength, string outputDirectory, bool includeDebug)
+ private bool ExtractExtent(int extentLocation, Encoding encoding, int blockLength, string outputDirectory, bool includeDebug)
{
// Check that directory exists in model
if (!DirectoryDescriptors.ContainsKey(extentLocation))
@@ -81,53 +92,86 @@ namespace SabreTools.Serialization.Wrappers
// 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);
+
+ // Recursively extract from LittleEndian extent location
+ ExtractExtent(dr.ExtentLocation.LittleEndian, 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 if ((dr.FileFlags & FileFlags.MULTI_EXTENT) == 0)
- {
- // 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);
- }
+ ExtractExtent(dr.ExtentLocation.BigEndian, encoding, blockLength, outDirTemp, includeDebug);
}
else
{
- if (includeDebug) Console.WriteLine("Extraction of multi-extent files is currently not supported");
+ // Record is a simple file extent, extract file
+ succeeded &= ExtractFile(dr, encoding, blockLength, false, outputDirectory, includeDebug);
+ // Also extract from BigEndian values if ambiguous
+ if (!dr.ExtentLocation.IsValid!)
+ succeeded &= ExtractFile(dr, encoding, blockLength, true, outputDirectory, includeDebug);
}
}
}
- return true;
+ return succeeded;
}
///
/// Extract file pointed to by a directory record
///
- private bool ExtractFile(DirectoryRecord dr, Dictionary extractedFiles, Encoding encoding, int blockLength, bool bigEndian, string outputDirectory, bool includeDebug)
+ private bool ExtractFile(DirectoryRecord dr, 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 + dr.ExtendedAttributeRecordLength) * blockLength;
// Check that the file hasn't been extracted already
- if (extractedFiles.ContainsKey(fileOffset))
+ if (extractedFiles.ContainsKey(dr.ExtentLocation))
return true;
+ // TODO: Decode properly (Use VD's separator characters and encoding)
+ string filename = encoding.GetString(dr.FileIdentifier);
+ int index = filename.LastIndexOf(';');
+ if (index > 0)
+ filename = filename.Substring(0, index);
+
+ // Ensure the full output directory exists
+ var filepath = Path.Combine(outputDirectory, filename);
+ var directoryName = Path.GetDirectoryName(filepath);
+ if (directoryName != null && !Directory.Exists(directoryName))
+ Directory.CreateDirectory(directoryName);
+
+ bool multiExtent = false;
+
+ // Currently cannot extract multi-extent or interleaved files
+ if ((dr.FileFlags & FileFlags.MULTI_EXTENT) != 0)
+ {
+ if (includeDebug) Console.WriteLine($"Extraction of multi-extent files is currently WIP: {filename}");
+ multiExtentFiles.Add(dr.FileIdentifier);
+ multiExtent = true;
+ }
+ else if (dr.FileUnitSize != 0 || dr.InterleaveGapSize != 0)
+ {
+ Console.WriteLine($"Extraction of interleaved files is currently not supported: {filename}");
+ extractedFiles.Add(dr.ExtentLocation, dr.ExtentLength);
+ return false;
+ }
+
+ // Check that the output file doesn't already exist
+ if (!multiExtent && (File.Exists(filepath) || Directory.Exists(filepath)))
+ {
+ // If it's the last extent of a multi-extent file, continue to append
+ if (multiExtentFiles.Exists(item => item.EqualsExactly(dr.FileIdentifier)))
+ {
+ if (includeDebug) Console.WriteLine($"File/Folder already exists, cannot extract: {filename}");
+ return false;
+ }
+ }
+
const int chunkSize = 2048 * 1024;
lock (_dataSourceLock)
{
+ long fileOffset = ((long)dr.ExtentLocation + (long)dr.ExtendedAttributeRecordLength) * (long)blockLength;
_dataSource.SeekIfPossible(fileOffset, SeekOrigin.Begin);
// Get the length, and make sure it won't EOF
@@ -135,28 +179,9 @@ namespace SabreTools.Serialization.Wrappers
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.LastIndexOf(';');
- 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);
-
- // 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}");
- using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
+ if (includeDebug) Console.WriteLine($"Extracting: {filepath}");
+ using var fs = File.Open(filepath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
while (length > 0)
{
int bytesToRead = (int)Math.Min(length, chunkSize);
@@ -169,7 +194,7 @@ namespace SabreTools.Serialization.Wrappers
}
// Mark the file as extracted
- extractedFiles.Add(fileOffset, dr.ExtentLength);
+ extractedFiles.Add(dr.ExtentLocation, dr.ExtentLength);
}
return true;