mirror of
https://github.com/SabreTools/SabreTools.Serialization.git
synced 2026-07-08 18:06:41 +00:00
Fix mszip relying on exceptions (#52)
* Better fix * initial * Fixed * Move function to proper location, clear memory better * work so far * more * more work * No more per file seeking * Finished work for now. * First round of fixes. * Return if array is empty too
This commit is contained in:
committed by
GitHub
parent
d7131dfa4c
commit
65a86d87f0
@@ -203,13 +203,13 @@ namespace SabreTools.Serialization.Readers
|
||||
dataBlock.Checksum = data.ReadUInt32LittleEndian();
|
||||
dataBlock.CompressedSize = data.ReadUInt16LittleEndian();
|
||||
dataBlock.UncompressedSize = data.ReadUInt16LittleEndian();
|
||||
|
||||
|
||||
if (dataReservedSize > 0)
|
||||
dataBlock.ReservedData = data.ReadBytes(dataReservedSize);
|
||||
data.SeekIfPossible(dataReservedSize, SeekOrigin.Current);
|
||||
|
||||
if (dataBlock.CompressedSize > 0)
|
||||
dataBlock.CompressedData = data.ReadBytes(dataBlock.CompressedSize);
|
||||
|
||||
data.SeekIfPossible(dataBlock.CompressedSize, SeekOrigin.Current);
|
||||
|
||||
return dataBlock;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.Data.Models.MicrosoftCabinet;
|
||||
using SabreTools.IO.Extensions;
|
||||
@@ -174,12 +175,22 @@ namespace SabreTools.Serialization.Wrappers
|
||||
// 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;
|
||||
}
|
||||
@@ -192,7 +203,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Extract the contents of a single folder
|
||||
/// </summary>
|
||||
@@ -217,15 +228,77 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
// Loop through the files
|
||||
bool allExtracted = true;
|
||||
var files = GetFiles(folderIndex, ignorePrev);
|
||||
var filterFiles = GetSpannedFiles(filename, folderIndex, ignorePrev);
|
||||
List<CFFILE> fileList = [];
|
||||
|
||||
// Filtering, add debug output eventually
|
||||
for (int i = 0; i < filterFiles.Length; i++)
|
||||
{
|
||||
var file = filterFiles[i];
|
||||
|
||||
if (file.FolderIndex == FolderIndex.CONTINUED_PREV_AND_NEXT ||
|
||||
file.FolderIndex == FolderIndex.CONTINUED_FROM_PREV)
|
||||
{
|
||||
// debug output for inconsistencies would go here
|
||||
continue;
|
||||
}
|
||||
|
||||
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 &= ExtractFile(outputDirectory, blockStream, file, includeDebug);
|
||||
|
||||
allExtracted &= ExtractFiles(outputDirectory, blockStream, file, includeDebug);
|
||||
}
|
||||
|
||||
return allExtracted;
|
||||
}
|
||||
|
||||
// TODO: this will apparently improve memory usage/performance, but it's not clear if this implementation is enough for that to happen
|
||||
/// <summary>
|
||||
/// Extract the contents of a single file, intended to be used with all files in a straight shot
|
||||
/// </summary>
|
||||
/// <param name="outputDirectory">Path to the output directory</param>
|
||||
/// <param name="blockStream">Stream representing the uncompressed block data</param>
|
||||
/// <param name="file">File information</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>True if the file extracted, false otherwise</returns>
|
||||
private static bool ExtractFiles(string outputDirectory, Stream blockStream, CFFILE file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract the contents of a single file
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.IO;
|
||||
using SabreTools.Data.Models.MicrosoftCabinet;
|
||||
using SabreTools.IO.Compression.MSZIP;
|
||||
using SabreTools.IO.Extensions;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
@@ -315,6 +316,8 @@ namespace SabreTools.Serialization.Wrappers
|
||||
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)
|
||||
@@ -335,7 +338,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
// Get all blocks from Prev
|
||||
if (Prev?.Header != null && Prev.Folders != null)
|
||||
{
|
||||
int prevFolderIndex = Prev.FolderCount;
|
||||
int prevFolderIndex = Prev.FolderCount - 1;
|
||||
var prevFolder = Prev.Folders[prevFolderIndex - 1];
|
||||
prevBlocks = Prev.GetDataBlocks(filename, prevFolder, prevFolderIndex, skipNext: true) ?? [];
|
||||
}
|
||||
@@ -360,6 +363,106 @@ namespace SabreTools.Serialization.Wrappers
|
||||
// Return all found blocks in order
|
||||
return [.. prevBlocks, .. folder.DataBlocks, .. nextBlocks];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads in all the datablocks for the current folder.
|
||||
/// </summary>
|
||||
/// <param name="folder">The folder to have the datablocks loaded for</param>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all files for the current folder, plus connected spanned folders.
|
||||
/// </summary>
|
||||
/// <param name="folderIndex">Index of the folder in the cabinet</param>
|
||||
/// <param name="ignorePrev">True to ignore previous links, false otherwise</param>
|
||||
/// <returns>Array of all files for the folder</returns>
|
||||
private CFFILE[] GetSpannedFiles(string? filename, int folderIndex, bool ignorePrev = false, bool skipPrev = false, bool skipNext = false)
|
||||
{
|
||||
// Ignore invalid archives
|
||||
if (Files.IsNullOrEmpty())
|
||||
return [];
|
||||
|
||||
// Get all files with a name and matching index
|
||||
var files = Array.FindAll(Files, f =>
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
int fileFolder = GetFolderIndex(f);
|
||||
return fileFolder == folderIndex;
|
||||
});
|
||||
|
||||
// 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
|
||||
CFFILE[] prevFiles = [];
|
||||
if (!skipPrev && spanPrev)
|
||||
{
|
||||
// Try to get Prev if it doesn't exist
|
||||
if (Prev?.Header == null)
|
||||
Prev = OpenPrevious(filename);
|
||||
|
||||
// Get all files from Prev
|
||||
if (Prev?.Header != null && Prev.Folders != null)
|
||||
{
|
||||
int prevFolderIndex = Prev.FolderCount - 1;
|
||||
prevFiles = Prev.GetSpannedFiles(filename, prevFolderIndex, skipNext: true) ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
// If the folder spans forward and Next is not being skipped
|
||||
CFFILE[] nextFiles = [];
|
||||
if (!skipNext && spanNext)
|
||||
{
|
||||
// Try to get Next if it doesn't exist
|
||||
if (Next?.Header == null)
|
||||
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) ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
// Return all found files in order
|
||||
return [.. prevFiles, .. files, .. nextFiles];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all files for the current folder index
|
||||
|
||||
Reference in New Issue
Block a user