diff --git a/SabreTools.Serialization/Readers/MicrosoftCabinet.cs b/SabreTools.Serialization/Readers/MicrosoftCabinet.cs index 0e66fdba..57a93965 100644 --- a/SabreTools.Serialization/Readers/MicrosoftCabinet.cs +++ b/SabreTools.Serialization/Readers/MicrosoftCabinet.cs @@ -171,8 +171,8 @@ namespace SabreTools.Serialization.Readers if (header.FolderReservedSize > 0) folder.ReservedData = data.ReadBytes(header.FolderReservedSize); - - if (folder.CabStartOffset > 0) + + /*if (folder.CabStartOffset > 0) { long currentPosition = data.Position; data.SeekIfPossible(folder.CabStartOffset, SeekOrigin.Begin); @@ -185,11 +185,11 @@ namespace SabreTools.Serialization.Readers } data.SeekIfPossible(currentPosition, SeekOrigin.Begin); - } + }*/ return folder; } - + /// /// Parse a Stream into a data block /// diff --git a/SabreTools.Serialization/Wrappers/MicrosoftCabinet.Extraction.cs b/SabreTools.Serialization/Wrappers/MicrosoftCabinet.Extraction.cs index f1923c34..9ab38bc4 100644 --- a/SabreTools.Serialization/Wrappers/MicrosoftCabinet.Extraction.cs +++ b/SabreTools.Serialization/Wrappers/MicrosoftCabinet.Extraction.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using SabreTools.Data.Models.MicrosoftCabinet; +using SabreTools.IO.Compression.MSZIP; using SabreTools.IO.Extensions; namespace SabreTools.Serialization.Wrappers @@ -30,8 +31,9 @@ namespace SabreTools.Serialization.Wrappers /// Open a cabinet set for reading, if possible /// /// Filename for one cabinet in the set + /// True to include debug data, false otherwise /// Wrapper representing the set, null on error - private static MicrosoftCabinet? OpenSet(string? filename) + private static MicrosoftCabinet? OpenSet(string? filename, bool includeDebug) { // If the file is invalid if (string.IsNullOrEmpty(filename)) @@ -52,7 +54,7 @@ namespace SabreTools.Serialization.Wrappers while (current.CabinetPrev != null) { // Attempt to open the previous cabinet - var prev = current.OpenPrevious(filename); + var prev = current.OpenPrevious(filename, includeDebug); if (prev?.Header == null) break; @@ -71,7 +73,7 @@ namespace SabreTools.Serialization.Wrappers break; // Open the next cabinet and try to parse - var next = current.OpenNext(filename); + var next = current.OpenNext(filename, includeDebug); if (next?.Header == null) break; @@ -89,7 +91,8 @@ namespace SabreTools.Serialization.Wrappers /// Open the next archive, if possible /// /// Filename for one cabinet in the set - private MicrosoftCabinet? OpenNext(string? filename) + /// True to include debug data, false otherwise + private MicrosoftCabinet? OpenNext(string? filename, bool includeDebug = false) { // Ignore invalid archives if (string.IsNullOrEmpty(filename)) @@ -102,22 +105,32 @@ namespace SabreTools.Serialization.Wrappers string? next = CabinetNext; if (string.IsNullOrEmpty(next)) return null; - + // Get the full next path string? folder = Path.GetDirectoryName(filename); if (folder != null) next = Path.Combine(folder, next); // Open and return the next cabinet - var fs = File.Open(next, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - return Create(fs); + // Catch exceptions due to file not existing, etc + try + { + var fs = File.Open(next, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + return Create(fs); + } + catch + { + if (includeDebug) Console.WriteLine($"Error: Cabinet set part {next} could not be opened!"); + return null; + } } /// /// Open the previous archive, if possible /// /// Filename for one cabinet in the set - private MicrosoftCabinet? OpenPrevious(string? filename) + /// True to include debug data, false otherwise + private MicrosoftCabinet? OpenPrevious(string? filename, bool includeDebug) { // Ignore invalid archives if (string.IsNullOrEmpty(filename)) @@ -130,15 +143,24 @@ namespace SabreTools.Serialization.Wrappers string? prev = CabinetPrev; if (string.IsNullOrEmpty(prev)) return null; - + // Get the full next path string? folder = Path.GetDirectoryName(filename); if (folder != null) prev = Path.Combine(folder, prev); // Open and return the previous cabinet - var fs = File.Open(prev, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - return Create(fs); + // Catch exceptions due to file not existing, etc + try + { + var fs = File.Open(prev, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + return Create(fs); + } + catch + { + if (includeDebug) Console.WriteLine($"Error: Cabinet set part {prev} could not be opened!"); + return null; + } } #endregion @@ -151,83 +173,45 @@ namespace SabreTools.Serialization.Wrappers // Display warning in debug runs if (includeDebug) Console.WriteLine("WARNING: LZX and Quantum compression schemes are not supported so some files may be skipped!"); - // Do not ignore previous links by default - bool ignorePrev = false; - // Open the full set if possible var cabinet = this; - if (Filename != null) + if (Filename == null) { - cabinet = OpenSet(Filename); - ignorePrev = true; + if (includeDebug) Console.WriteLine($"Cabinet set could not be opened!"); + return false; } - + + cabinet = OpenSet(Filename, includeDebug); + if (cabinet == null) + return false; + + // If we have anything but the first file, avoid extraction to avoid repeat extracts + // TODO: if/when full msi support is added, somehow this is going to have to take that into account, while also still handling partial sets + if (this.Filename != cabinet.Filename) + { + string firstCabName = Path.GetFileName(cabinet.Filename) ?? string.Empty; + if (includeDebug) Console.WriteLine($"Only the first cabinet {firstCabName} will be extracted!"); + return false; + } + // If the archive is invalid if (cabinet?.Folders == null || cabinet.Folders.Length == 0) return false; - - try - { - // Loop through the folders - bool allExtracted = true; - while (true) - { - // Loop through the current folders - for (int f = 0; f < cabinet.Folders.Length; f++) - { - if (f == 0 && (cabinet.Files[0].FolderIndex == FolderIndex.CONTINUED_PREV_AND_NEXT - || cabinet.Files[0].FolderIndex == FolderIndex.CONTINUED_FROM_PREV)) - continue; - - var folder = cabinet.Folders[f]; - allExtracted &= cabinet.ExtractFolder(Filename, outputDirectory, folder, f, ignorePrev, includeDebug); - } - - // Move to the next cabinet, if possible - Array.ForEach(cabinet.Folders, folder => folder.DataBlocks = []); - - cabinet = cabinet.Next; - cabinet?.Prev = null; - - // TODO: already-extracted data isn't being cleared from memory, at least not nearly enough. - if (cabinet?.Folders == null || cabinet.Folders.Length == 0) - break; - } - - return allExtracted; - } - catch (Exception ex) - { - if (includeDebug) Console.Error.WriteLine(ex); - return false; - } + + return cabinet.ExtractSet(Filename, outputDirectory, includeDebug); } /// - /// Extract the contents of a single folder + /// Get filtered array of spanned files for a folder /// /// Filename for one cabinet in the set, if available - /// Path to the output directory - /// Folder containing the blocks to decompress /// Index of the folder in the cabinet - /// True to ignore previous links, false otherwise /// True to include debug data, false otherwise - /// True if all files extracted, false otherwise - private bool ExtractFolder(string? filename, - string outputDirectory, - CFFOLDER? folder, - int folderIndex, - bool ignorePrev, - bool includeDebug) + /// Filtered array of files + private CFFILE[] GetSpannedFilesArray(string? filename, int folderIndex, bool includeDebug) { - // Decompress the blocks, if possible - using var blockStream = DecompressBlocks(filename, folder, folderIndex, includeDebug); - if (blockStream == null || blockStream.Length == 0) - return false; - // Loop through the files - bool allExtracted = true; - var filterFiles = GetSpannedFiles(filename, folderIndex, ignorePrev); + var filterFiles = GetSpannedFiles(filename, folderIndex, includeDebug); List fileList = []; // Filtering, add debug output eventually @@ -245,99 +229,302 @@ namespace SabreTools.Serialization.Wrappers fileList.Add(file); } - CFFILE[] files = fileList.ToArray(); - blockStream.SeekIfPossible(0, SeekOrigin.Begin); - for (int i = 0; i < files.Length; i++) - { - var file = files[i]; - allExtracted &= ExtractFiles(outputDirectory, blockStream, file, includeDebug); - } - - return allExtracted; + return fileList.ToArray(); } - // TODO: this will apparently improve memory usage/performance, but it's not clear if this implementation is enough for that to happen /// - /// Extract the contents of a single file, intended to be used with all files in a straight shot + /// Get stream representing the output file /// + /// Filename for the file that will be extracted to /// Path to the output directory - /// Stream representing the uncompressed block data - /// File information - /// True to include debug data, false otherwise - /// True if the file extracted, false otherwise - private static bool ExtractFiles(string outputDirectory, Stream blockStream, CFFILE file, bool includeDebug) + /// Filestream opened for the file + private FileStream GetFileStream(string filename, string outputDirectory) { + // Ensure directory separators are consistent + if (Path.DirectorySeparatorChar == '\\') + filename = filename.Replace('/', '\\'); + else if (Path.DirectorySeparatorChar == '/') + filename = filename.Replace('\\', '/'); + + // 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); + + // Open the output file for writing + return File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None); + } + + /// + /// Read a datablock from a cabinet + /// + /// Offset to be read from + /// True to include debug data, false otherwise + /// Read datablock + private CFDATA? ReadBlock(ref long offset, bool includeDebug) + { + + // Should only ever occur if it tries to read more than the file, but good to catch in general + try + { + lock (this._dataSourceLock) + { + this._dataSource.SeekIfPossible(offset, SeekOrigin.Begin); + var dataBlock = new CFDATA(); + + var dataReservedSize = this.Header.DataReservedSize; + + dataBlock.Checksum = this._dataSource.ReadUInt32LittleEndian(); + dataBlock.CompressedSize = this._dataSource.ReadUInt16LittleEndian(); + dataBlock.UncompressedSize = this._dataSource.ReadUInt16LittleEndian(); + + if (dataReservedSize > 0) + dataBlock.ReservedData = this._dataSource.ReadBytes(dataReservedSize); + + if (dataBlock.CompressedSize > 0) + dataBlock.CompressedData = this._dataSource.ReadBytes(dataBlock.CompressedSize); + + offset = _dataSource.Position; + + return dataBlock; + } + } + catch (Exception ex) + { + if (includeDebug) Console.Error.WriteLine(ex); + return null; + } + + } + + /// + /// Extract the contents of a cabinet set + /// + /// Filename for one cabinet in the set, if available + /// Path to the output directory + /// True to include debug data, false otherwise + /// True if all files extracted, false otherwise + private bool ExtractSet(string? cabFilename, string outputDirectory, bool includeDebug) + { + var cabinet = this; + var currentCabFilename = cabFilename; + long offset = 0; try { - byte[] fileData = blockStream.ReadBytes((int)file.FileSize); + // Loop through the folders + bool allExtracted = true; + while (true) + { + // Loop through the current folders + for (int f = 0; f < cabinet.Folders.Length; f++) + { + if (f == 0 && (cabinet.Files[0].FolderIndex == FolderIndex.CONTINUED_PREV_AND_NEXT + || cabinet.Files[0].FolderIndex == FolderIndex.CONTINUED_FROM_PREV)) + { + continue; + } - // Ensure directory separators are consistent - string filename = file.Name; - if (Path.DirectorySeparatorChar == '\\') - filename = filename.Replace('/', '\\'); - else if (Path.DirectorySeparatorChar == '/') - filename = filename.Replace('\\', '/'); + var folder = cabinet.Folders[f]; + CFFILE[] files = cabinet.GetSpannedFilesArray(currentCabFilename, f, includeDebug); + var file = files[0]; + int bytesLeft = (int)file.FileSize; + int fileCounter = 0; + + // Cache starting position + offset = folder.CabStartOffset; + + var mszip = Decompressor.Create(); + try + { + // Ensure folder contains data + if (folder.DataCount == 0) + return false; - // 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); + // Skip unsupported compression types to avoid opening a blank filestream. This can be altered/removed if these types are ever supported. + var compressionType = GetCompressionType(folder); + if (compressionType == CompressionType.TYPE_QUANTUM || compressionType == CompressionType.TYPE_LZX) + continue; + + var fs = GetFileStream(file.Name, outputDirectory); - // Open the output file for writing - using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None); - fs.Write(fileData, 0, fileData.Length); - fs.Flush(); + //uint quantumWindowBits = (uint)(((ushort)folder.CompressionType >> 8) & 0x1f); + + if (folder.CabStartOffset <= 0) + return false; + + var tempCabinet = cabinet; + int j = 0; + + // Loop through the data blocks + // Has to be a while loop instead of a for loop due to cab spanning continue blocks + while (j < folder.DataCount) + { + var dataBlock = tempCabinet.ReadBlock(ref offset, includeDebug); + if (dataBlock == null) + { + if (includeDebug) Console.Error.WriteLine($"Error extracting file {file.Name}"); + break; + } + + // Get the data to be processed + byte[] blockData = dataBlock.CompressedData; + + // If the block is continued, append + bool continuedBlock = false; + if (dataBlock.UncompressedSize == 0) + { + tempCabinet = tempCabinet.Next; + if (tempCabinet == null) + break; // Next cab is missing, continue + + // CompressionType not updated because there's no way it's possible that it can swap on continued blocks + folder = tempCabinet.Folders[0]; + offset = folder.CabStartOffset; + var nextBlock = tempCabinet.ReadBlock(ref offset, includeDebug); + if (nextBlock == null) + { + if (includeDebug) Console.Error.WriteLine($"Error extracting file {file.Name}"); + break; + } + + byte[] nextData = nextBlock.CompressedData; + if (nextData.Length == 0) + continue; + + continuedBlock = true; + blockData = [.. blockData, .. nextData]; + dataBlock.CompressedSize += nextBlock.CompressedSize; + dataBlock.UncompressedSize = nextBlock.UncompressedSize; + } + + // Get the uncompressed data block + byte[] data = compressionType switch + { + CompressionType.TYPE_NONE => blockData, + CompressionType.TYPE_MSZIP => DecompressMSZIPBlock(f, mszip, j, dataBlock, blockData, includeDebug), + + // TODO: Unsupported + CompressionType.TYPE_QUANTUM => [], + CompressionType.TYPE_LZX => [], + + // Should be impossible + _ => [], + }; + + if (bytesLeft > 0 && bytesLeft >= data.Length) + { + fs.Write(data); + bytesLeft -= data.Length; + } + else if (bytesLeft > 0 && bytesLeft < data.Length) + { + int tempBytesLeft = bytesLeft; + fs.Write(data, 0, bytesLeft); + fs.Close(); + + // reached end of folder + if (fileCounter + 1 == files.Length) + break; + + file = files[++fileCounter]; + bytesLeft = (int)file.FileSize; + fs = GetFileStream(file.Name, outputDirectory); + while (bytesLeft < data.Length - tempBytesLeft) + { + fs.Write(data, tempBytesLeft, bytesLeft); + tempBytesLeft += bytesLeft; + fs.Close(); + + // reached end of folder + if (fileCounter + 1 == files.Length) + break; + + file = files[++fileCounter]; + bytesLeft = (int)file.FileSize; + fs = GetFileStream(file.Name, outputDirectory); + } + + fs.Write(data, tempBytesLeft, data.Length - tempBytesLeft); + bytesLeft -= (data.Length - tempBytesLeft); + } + else + { + int tempBytesLeft = bytesLeft; + fs.Close(); + + // reached end of folder + if (fileCounter + 1 == files.Length) + break; + + file = files[++fileCounter]; + bytesLeft = (int)file.FileSize; + fs = GetFileStream(file.Name, outputDirectory); + while (bytesLeft < data.Length - tempBytesLeft) + { + fs.Write(data, tempBytesLeft, bytesLeft); + tempBytesLeft += bytesLeft; + fs.Close(); + + // reached end of folder + if (fileCounter + 1 == files.Length) + break; + + file = files[++fileCounter]; + bytesLeft = (int)file.FileSize; + fs = GetFileStream(file.Name, outputDirectory); + } + + fs.Write(data, tempBytesLeft, data.Length - tempBytesLeft); + bytesLeft -= (data.Length - tempBytesLeft); + } + + // Top if block occurs on http://redump.org/disc/107833/ , middle on https://dbox.tools/titles/pc/57520FA0 , bottom still unobserved + // While loop since this also handles 0 byte files. Example file seen in http://redump.org/disc/93312/ , cab Group17.cab, file TRACKSLOC6DYNTEX_BIN + while (bytesLeft == 0) + { + fs.Close(); + + // reached end of folder + if (fileCounter + 1 == files.Length) + break; + + file = files[++fileCounter]; + bytesLeft = (int)file.FileSize; + fs = GetFileStream(file.Name, outputDirectory); + } + + if (continuedBlock) + j = 0; + + j++; + } + } + catch (Exception ex) + { + if (includeDebug) Console.Error.WriteLine(ex); + return false; + } + } + + // Move to the next cabinet, if possible + cabinet = cabinet.Next; + if (cabinet == null) // If the next cabinet is missing, there's no better way to handle this + return false; + + currentCabFilename = cabinet.Filename; + + if (cabinet.Folders.Length == 0) + break; + } + + return allExtracted; } catch (Exception ex) { if (includeDebug) Console.Error.WriteLine(ex); return false; } - - return true; - } - - /// - /// Extract the contents of a single file - /// - /// Path to the output directory - /// Stream representing the uncompressed block data - /// File information - /// True to include debug data, false otherwise - /// True if the file extracted, false otherwise - private static bool ExtractFile(string outputDirectory, Stream blockStream, CFFILE file, bool includeDebug) - { - try - { - blockStream.SeekIfPossible(file.FolderStartOffset, SeekOrigin.Begin); - byte[] fileData = blockStream.ReadBytes((int)file.FileSize); - - // Ensure directory separators are consistent - string filename = file.Name; - if (Path.DirectorySeparatorChar == '\\') - filename = filename.Replace('/', '\\'); - else if (Path.DirectorySeparatorChar == '/') - filename = filename.Replace('\\', '/'); - - // 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); - - // Open the output file for writing - using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None); - fs.Write(fileData, 0, fileData.Length); - fs.Flush(); - } - catch (Exception ex) - { - if (includeDebug) Console.Error.WriteLine(ex); - return false; - } - - return true; } #endregion @@ -380,4 +567,4 @@ namespace SabreTools.Serialization.Wrappers #endregion } -} +} \ No newline at end of file diff --git a/SabreTools.Serialization/Wrappers/MicrosoftCabinet.cs b/SabreTools.Serialization/Wrappers/MicrosoftCabinet.cs index 5a8c678b..99432d04 100644 --- a/SabreTools.Serialization/Wrappers/MicrosoftCabinet.cs +++ b/SabreTools.Serialization/Wrappers/MicrosoftCabinet.cs @@ -173,77 +173,6 @@ namespace SabreTools.Serialization.Wrappers #region Folders - /// - /// Decompress all blocks for a folder - /// - /// Filename for one cabinet in the set, if available - /// Folder containing the blocks to decompress - /// Index of the folder in the cabinet - /// True to include debug data, false otherwise - /// Stream representing the decompressed data on success, null otherwise - public Stream? DecompressBlocks(string? filename, CFFOLDER? folder, int folderIndex, bool includeDebug) - { - // Ensure data blocks - var dataBlocks = GetDataBlocks(filename, folder, folderIndex); - if (dataBlocks == null || dataBlocks.Length == 0) - return null; - - // Get the compression type - var compressionType = GetCompressionType(folder!); - - // Setup decompressors - var mszip = Decompressor.Create(); - //uint quantumWindowBits = (uint)(((ushort)folder.CompressionType >> 8) & 0x1f); - - // Loop through the data blocks - var ms = new MemoryStream(); - for (int i = 0; i < dataBlocks.Length; i++) - { - var db = dataBlocks[i]; - - // Get the data to be processed - byte[] blockData = db.CompressedData; - - // If the block is continued, append - bool continuedBlock = false; - if (db.UncompressedSize == 0) - { - var nextBlock = dataBlocks[i + 1]; - byte[]? nextData = nextBlock.CompressedData; - if (nextData == null) - continue; - - continuedBlock = true; - blockData = [.. blockData, .. nextData]; - db.CompressedSize += nextBlock.CompressedSize; - db.UncompressedSize = nextBlock.UncompressedSize; - } - - // Get the uncompressed data block - byte[] data = compressionType switch - { - CompressionType.TYPE_NONE => blockData, - CompressionType.TYPE_MSZIP => DecompressMSZIPBlock(folderIndex, mszip, i, db, blockData, includeDebug), - - // TODO: Unsupported - CompressionType.TYPE_QUANTUM => [], - CompressionType.TYPE_LZX => [], - - // Should be impossible - _ => [], - }; - - // Write the uncompressed data block - ms.Write(data, 0, data.Length); - ms.Flush(); - - // Increment additionally if we had a continued block - if (continuedBlock) i++; - } - - return ms; - } - /// /// Decompress an MS-ZIP block using an existing decompressor /// @@ -301,104 +230,20 @@ namespace SabreTools.Serialization.Wrappers return (CompressionType)ushort.MaxValue; } - /// - /// Get the set of data blocks for a folder - /// - /// Filename for one cabinet in the set, if available - /// Folder containing the blocks to decompress - /// Index of the folder in the cabinet - /// Indicates if previous cabinets should be ignored - /// Indicates if next cabinets should be ignored - /// Array of data blocks on success, null otherwise - private CFDATA[]? GetDataBlocks(string? filename, CFFOLDER? folder, int folderIndex, bool skipPrev = false, bool skipNext = false) - { - // Skip invalid folders - if (folder?.DataBlocks == null || folder.DataBlocks.Length == 0) - return null; - - GetData(folder); - - // Get all files for the folder - var files = GetFiles(folderIndex); - if (files.Length == 0) - return folder.DataBlocks; - - // Check if the folder spans in either direction - bool spanPrev = Array.Exists(files, f => f.FolderIndex == FolderIndex.CONTINUED_FROM_PREV || f.FolderIndex == FolderIndex.CONTINUED_PREV_AND_NEXT); - bool spanNext = Array.Exists(files, f => f.FolderIndex == FolderIndex.CONTINUED_TO_NEXT || f.FolderIndex == FolderIndex.CONTINUED_PREV_AND_NEXT); - - // If the folder spans backward and Prev is not being skipped - CFDATA[] prevBlocks = []; - if (!skipPrev && spanPrev) - { - // Try to get Prev if it doesn't exist - if (Prev?.Header == null) - Prev = OpenPrevious(filename); - - // Get all blocks from Prev - if (Prev?.Header != null && Prev.Folders != null) - { - int prevFolderIndex = Prev.FolderCount - 1; - var prevFolder = Prev.Folders[prevFolderIndex - 1]; - prevBlocks = Prev.GetDataBlocks(filename, prevFolder, prevFolderIndex, skipNext: true) ?? []; - } - } - - // If the folder spans forward and Next is not being skipped - CFDATA[] nextBlocks = []; - if (!skipNext && spanNext) - { - // Try to get Next if it doesn't exist - if (Next?.Header == null) - Next = OpenNext(filename); - - // Get all blocks from Prev - if (Next?.Header != null && Next.Folders != null) - { - var nextFolder = Next.Folders[0]; - nextBlocks = Next.GetDataBlocks(filename, nextFolder, 0, skipPrev: true) ?? []; - } - } - - // Return all found blocks in order - return [.. prevBlocks, .. folder.DataBlocks, .. nextBlocks]; - } - - /// - /// Loads in all the datablocks for the current folder. - /// - /// The folder to have the datablocks loaded for - public void GetData(CFFOLDER folder) - { - if (folder.CabStartOffset <= 0) - return; - - uint offset = folder.CabStartOffset; - for (int i = 0; i < folder.DataCount; i++) - { - offset += 8; - - if (Header.DataReservedSize > 0) - { - folder.DataBlocks[i].ReservedData = ReadRangeFromSource(offset, Header.DataReservedSize); - offset += Header.DataReservedSize; - } - - if (folder.DataBlocks[i].CompressedSize > 0) - { - folder.DataBlocks[i].CompressedData = ReadRangeFromSource(offset, folder.DataBlocks[i].CompressedSize); - offset += folder.DataBlocks[i].CompressedSize; - } - } - } - /// /// Get all files for the current folder, plus connected spanned folders. /// + /// Input filename of the cabinet to read from /// Index of the folder in the cabinet - /// True to ignore previous links, false otherwise + /// True to include debug data, false otherwise + /// True if previous cabinets should be skipped, false otherwise. + /// True if next cabinets should be skipped, false otherwise. /// Array of all files for the folder - private CFFILE[] GetSpannedFiles(string? filename, int folderIndex, bool ignorePrev = false, bool skipPrev = false, bool skipNext = false) + private CFFILE[] GetSpannedFiles(string? filename, + int folderIndex, + bool includeDebug, + bool skipPrev = false, + bool skipNext = false) { // Ignore invalid archives if (Files.IsNullOrEmpty()) @@ -410,14 +255,12 @@ namespace SabreTools.Serialization.Wrappers if (string.IsNullOrEmpty(f.Name)) return false; - // Ignore links to previous cabinets, if required - if (ignorePrev) - { - if (f.FolderIndex == FolderIndex.CONTINUED_FROM_PREV) - return false; - else if (f.FolderIndex == FolderIndex.CONTINUED_PREV_AND_NEXT) - return false; - } + // Ignore links to previous cabinets + if (f.FolderIndex == FolderIndex.CONTINUED_FROM_PREV) + return false; + else if (f.FolderIndex == FolderIndex.CONTINUED_PREV_AND_NEXT) + return false; + int fileFolder = GetFolderIndex(f); return fileFolder == folderIndex; @@ -433,13 +276,13 @@ namespace SabreTools.Serialization.Wrappers { // Try to get Prev if it doesn't exist if (Prev?.Header == null) - Prev = OpenPrevious(filename); + Prev = OpenPrevious(filename, includeDebug); // Get all files from Prev if (Prev?.Header != null && Prev.Folders != null) { int prevFolderIndex = Prev.FolderCount - 1; - prevFiles = Prev.GetSpannedFiles(filename, prevFolderIndex, skipNext: true) ?? []; + prevFiles = Prev.GetSpannedFiles(filename, prevFolderIndex, includeDebug, skipNext: true) ?? []; } } @@ -449,13 +292,13 @@ namespace SabreTools.Serialization.Wrappers { // Try to get Next if it doesn't exist if (Next?.Header == null) - Next = OpenNext(filename); - + Next = OpenNext(filename); + // Get all files from Prev if (Next?.Header != null && Next.Folders != null) { var nextFolder = Next.Folders[0]; - nextFiles = Next.GetSpannedFiles(filename, 0, skipPrev: true) ?? []; + nextFiles = Next.GetSpannedFiles(filename, 0, includeDebug, skipPrev: true) ?? []; } }