From b7fceee2b726e58897cb8776e75274a46cb8f2e1 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 28 Jul 2025 15:22:28 -0400 Subject: [PATCH] Port more MS-CAB helpers from BOS --- .../Wrappers/MicrosoftCabinet.cs | 104 ++++++++++++++++-- 1 file changed, 93 insertions(+), 11 deletions(-) diff --git a/SabreTools.Serialization/Wrappers/MicrosoftCabinet.cs b/SabreTools.Serialization/Wrappers/MicrosoftCabinet.cs index dba57bbd..3957fb04 100644 --- a/SabreTools.Serialization/Wrappers/MicrosoftCabinet.cs +++ b/SabreTools.Serialization/Wrappers/MicrosoftCabinet.cs @@ -1,9 +1,10 @@ using System; using System.IO; +using SabreTools.Models.MicrosoftCabinet; namespace SabreTools.Serialization.Wrappers { - public partial class MicrosoftCabinet : WrapperBase + public partial class MicrosoftCabinet : WrapperBase { #region Descriptive Properties @@ -14,20 +15,20 @@ namespace SabreTools.Serialization.Wrappers #region Extension Properties - /// - public Models.MicrosoftCabinet.CFFILE[]? Files => Model.Files; + /// + public CFFILE[]? Files => Model.Files; - /// + /// public int FileCount => Model.Header?.FileCount ?? 0; - /// - public Models.MicrosoftCabinet.CFFOLDER[]? Folders => Model.Folders; + /// + public CFFOLDER[]? Folders => Model.Folders; - /// + /// public int FolderCount => Model.Header?.FolderCount ?? 0; - /// - public Models.MicrosoftCabinet.CFHEADER? Header => Model.Header; + /// + public CFHEADER? Header => Model.Header; /// /// Reference to the next cabinet header @@ -46,14 +47,14 @@ namespace SabreTools.Serialization.Wrappers #region Constructors /// - public MicrosoftCabinet(Models.MicrosoftCabinet.Cabinet? model, byte[]? data, int offset) + public MicrosoftCabinet(Cabinet? model, byte[]? data, int offset) : base(model, data, offset) { // All logic is handled by the base class } /// - public MicrosoftCabinet(Models.MicrosoftCabinet.Cabinet? model, Stream? data) + public MicrosoftCabinet(Cabinet? model, Stream? data) : base(model, data) { // All logic is handled by the base class @@ -298,6 +299,87 @@ namespace SabreTools.Serialization.Wrappers } } + /// + /// Get the corrected folder index + /// + public int GetFolderIndex(CFFILE file) + { + return file.FolderIndex switch + { + FolderIndex.CONTINUED_FROM_PREV => 0, + FolderIndex.CONTINUED_TO_NEXT => (Header?.FolderCount ?? 1) - 1, + FolderIndex.CONTINUED_PREV_AND_NEXT => 0, + _ => (int)file.FolderIndex, + }; + } + + #endregion + + #region Folders + + /// + /// Get the set of data blocks for a folder + /// + public CFDATA[]? GetDataBlocks(string file, CFFOLDER? folder, int folderIndex, bool skipPrev = false, bool skipNext = false) + { + // Skip invalid folders + if (folder?.DataBlocks == null || folder.DataBlocks.Length == 0) + return null; + + // Get all files for the folder + var files = GetFiles(folderIndex); + if (files.Length == 0) + return folder.DataBlocks; + + // Check if the folder spans backward + CFDATA[] prevBlocks = []; + if (!skipPrev && Array.Exists(files, f => f.FolderIndex == FolderIndex.CONTINUED_FROM_PREV || f.FolderIndex == FolderIndex.CONTINUED_PREV_AND_NEXT)) + { + var prev = OpenPrevious(file); + if (prev?.Model?.Header != null && prev.Model.Folders != null) + { + int prevFolderIndex = prev.Model.Header.FolderCount; + var prevFolder = prev.Model.Folders[prevFolderIndex - 1]; + prevBlocks = prev.GetDataBlocks(file, prevFolder, prevFolderIndex, skipNext: true) ?? []; + } + } + + // Check if the folder spans forward + CFDATA[] nextBlocks = []; + if (!skipNext && Array.Exists(files, f => f.FolderIndex == FolderIndex.CONTINUED_TO_NEXT || f.FolderIndex == FolderIndex.CONTINUED_PREV_AND_NEXT)) + { + var next = OpenNext(file); + if (next?.Model?.Header != null && next.Model.Folders != null) + { + var nextFolder = next.Model.Folders[0]; + nextBlocks = next.GetDataBlocks(file, nextFolder, 0, skipPrev: true) ?? []; + } + } + + // Return all found blocks in order + return [.. prevBlocks, .. folder.DataBlocks, .. nextBlocks]; + } + + /// + /// Get all files for the current folder index + /// + public CFFILE[] GetFiles(int folderIndex) + { + // Ignore invalid archives + if (Files == null) + return []; + + // Get all files with a name and matching index + return Array.FindAll(Files, f => + { + if (string.IsNullOrEmpty(f.Name)) + return false; + + int fileFolder = GetFolderIndex(f); + return fileFolder == folderIndex; + }); + } + #endregion } }