Files
SabreTools.Serialization/SabreTools.Wrappers/InstallShieldCabinet.cs
Matt Nadareski 7689c6dd07 Libraries
This change looks dramatic, but it's just separating out the already-split namespaces into separate top-level folders. In theory, every single one could be built into their own Nuget package. `SabreTools.Serialization` still builds the normal Nuget package that is used by all other projects and includes all namespaces.
2026-03-21 16:26:56 -04:00

360 lines
11 KiB
C#

using System;
using System.IO;
using SabreTools.Data.Extensions;
using SabreTools.Data.Models.InstallShieldCabinet;
namespace SabreTools.Wrappers
{
public partial class InstallShieldCabinet : WrapperBase<Cabinet>
{
#region Descriptive Properties
/// <inheritdoc/>
public override string DescriptionString => "InstallShield Cabinet";
#endregion
#region Extension Properties
/// <inheritdoc cref="Cabinet.CommonHeader"/>
public CommonHeader CommonHeader => Model.CommonHeader;
/// <inheritdoc cref="Cabinet.Components"/>
public Component[] Components => Model.Components;
/// <summary>
/// Number of components in the cabinet set
/// </summary>
public int ComponentCount => Components.Length;
/// <summary>
/// Number of directories in the cabinet set
/// </summary>
public ushort DirectoryCount => Model.Descriptor.DirectoryCount;
/// <inheritdoc cref="Cabinet.DirectoryNames"/>
public string[] DirectoryNames => Model.DirectoryNames;
/// <summary>
/// Number of files in the cabinet set
/// </summary>
public uint FileCount => Model.Descriptor.FileCount;
/// <inheritdoc cref="Cabinet.FileDescriptors"/>
public FileDescriptor[] FileDescriptors => Model.FileDescriptors;
/// <inheritdoc cref="Cabinet.FileGroups"/>
public FileGroup[] FileGroups => Model.FileGroups;
/// <summary>
/// Number of file groups in the cabinet set
/// </summary>
public int FileGroupCount => Model.FileGroups.Length;
/// <summary>
/// Indicates if Unicode strings are used
/// </summary>
public bool IsUnicode => MajorVersion >= 17;
/// <summary>
/// The major version of the cabinet
/// </summary>
public int MajorVersion => Model.GetMajorVersion();
/// <inheritdoc cref="Cabinet.VolumeHeader"/>
public VolumeHeader VolumeHeader => Model.VolumeHeader;
#endregion
#region Constructors
/// <inheritdoc/>
public InstallShieldCabinet(Cabinet model, byte[] data) : base(model, data) { }
/// <inheritdoc/>
public InstallShieldCabinet(Cabinet model, byte[] data, int offset) : base(model, data, offset) { }
/// <inheritdoc/>
public InstallShieldCabinet(Cabinet model, byte[] data, int offset, int length) : base(model, data, offset, length) { }
/// <inheritdoc/>
public InstallShieldCabinet(Cabinet model, Stream data) : base(model, data) { }
/// <inheritdoc/>
public InstallShieldCabinet(Cabinet model, Stream data, long offset) : base(model, data, offset) { }
/// <inheritdoc/>
public InstallShieldCabinet(Cabinet model, Stream data, long offset, long length) : base(model, data, offset, length) { }
#endregion
#region Static Constructors
/// <summary>
/// Create an InstallShield Cabinet from a byte array and offset
/// </summary>
/// <param name="data">Byte array representing the cabinet</param>
/// <param name="offset">Offset within the array to parse</param>
/// <returns>A cabinet wrapper on success, null on failure</returns>
public static InstallShieldCabinet? Create(byte[]? data, int offset)
{
// If the data is invalid
if (data is null || data.Length == 0)
return null;
// If the offset is out of bounds
if (offset < 0 || offset >= data.Length)
return null;
// Create a memory stream and use that
var dataStream = new MemoryStream(data, offset, data.Length - offset);
return Create(dataStream);
}
/// <summary>
/// Create a InstallShield Cabinet from a Stream
/// </summary>
/// <param name="data">Stream representing the cabinet</param>
/// <returns>A cabinet wrapper on success, null on failure</returns>
public static InstallShieldCabinet? Create(Stream? data)
{
// If the data is invalid
if (data is null || !data.CanRead)
return null;
try
{
// Cache the current offset
long currentOffset = data.Position;
var model = new Serialization.Readers.InstallShieldCabinet().Deserialize(data);
if (model is null)
return null;
return new InstallShieldCabinet(model, data, currentOffset);
}
catch
{
return null;
}
}
#endregion
#region Component
/// <summary>
/// Get the component name at a given index, if possible
/// </summary>
public string? GetComponentName(int index)
{
if (Components is null)
return null;
if (index < 0 || index >= ComponentCount)
return null;
var component = Components[index];
if (component?.Identifier is null)
return null;
return component.Identifier.Replace('\\', '/');
}
#endregion
#region Directory
/// <summary>
/// Get the directory name at a given index, if possible
/// </summary>
public string? GetDirectoryName(int index)
{
if (index < 0 || index >= DirectoryNames.Length)
return null;
return DirectoryNames[index];
}
/// <summary>
/// Get the directory index for the given file index
/// </summary>
/// <returns>Directory index if found, UInt32.MaxValue on error</returns>
public uint GetDirectoryIndexFromFile(int index)
{
FileDescriptor? descriptor = GetFileDescriptor(index);
if (descriptor is not null)
return descriptor.DirectoryIndex;
else
return uint.MaxValue;
}
#endregion
#region File
/// <summary>
/// Returns if the file at a given index is marked as valid
/// </summary>
public bool FileIsValid(int index)
{
if (index < 0 || index > FileCount)
return false;
FileDescriptor? descriptor = GetFileDescriptor(index);
if (descriptor is null)
return false;
if (descriptor.IsInvalid())
return false;
if (descriptor.NameOffset == default)
return false;
if (descriptor.DataOffset == default)
return false;
return true;
}
/// <summary>
/// Get the reported expanded file size for a given index
/// </summary>
public ulong GetExpandedFileSize(int index)
{
FileDescriptor? descriptor = GetFileDescriptor(index);
if (descriptor is not null)
return descriptor.ExpandedSize;
else
return 0;
}
/// <summary>
/// Get the file descriptor at a given index, if possible
/// </summary>
public FileDescriptor? GetFileDescriptor(int index)
{
if (index < 0 || index >= FileDescriptors.Length)
return null;
return FileDescriptors[index];
}
/// <summary>
/// Get the file descriptor at a given index, if possible
/// </summary>
/// <remarks>Verifies the file descriptor flags before returning</remarks>
public bool TryGetFileDescriptor(int index, out FileDescriptor? fileDescriptor)
{
fileDescriptor = GetFileDescriptor(index);
if (fileDescriptor is null)
{
Console.Error.WriteLine($"Failed to get file descriptor for file {index}");
return false;
}
if (fileDescriptor.IsInvalid() || fileDescriptor.DataOffset == 0)
{
Console.Error.WriteLine($"File at {index} is marked as invalid");
return false;
}
return true;
}
/// <summary>
/// Get the file name at a given index, if possible
/// </summary>
public string? GetFileName(int index)
{
var descriptor = GetFileDescriptor(index);
if (descriptor is null || descriptor.IsInvalid())
return null;
return descriptor.Name;
}
/// <summary>
/// Get the packed size of a file, if possible
/// </summary>
public static ulong GetReadableBytes(FileDescriptor? descriptor)
{
if (descriptor is null)
return 0;
return descriptor.IsCompressed()
? descriptor.CompressedSize
: descriptor.ExpandedSize;
}
/// <summary>
/// Get the packed size of a file, if possible
/// </summary>
public static ulong GetWritableBytes(FileDescriptor? descriptor)
{
if (descriptor is null)
return 0;
return descriptor.ExpandedSize;
}
#endregion
#region File Group
/// <summary>
/// Get the file group at a given index, if possible
/// </summary>
public FileGroup? GetFileGroup(int index)
{
if (index < 0 || index >= FileGroups.Length)
return null;
return FileGroups[index];
}
/// <summary>
/// Get the file group at a given name, if possible
/// </summary>
public FileGroup? GetFileGroup(string name)
=> Array.Find(FileGroups, fg => string.Equals(fg.Name, name));
/// <summary>
/// Get the file group for the given file index, if possible
/// </summary>
public FileGroup? GetFileGroupFromFile(int index)
{
if (index < 0 || index >= FileCount)
return null;
for (int i = 0; i < FileGroupCount; i++)
{
var fileGroup = GetFileGroup(i);
if (fileGroup is null)
continue;
if (fileGroup.FirstFile > index || fileGroup.LastFile < index)
continue;
return fileGroup;
}
return null;
}
/// <summary>
/// Get the file group name at a given index, if possible
/// </summary>
public string? GetFileGroupName(int index)
=> GetFileGroup(index)?.Name;
/// <summary>
/// Get the file group name at a given file index, if possible
/// </summary>
public string? GetFileGroupNameFromFile(int index)
=> GetFileGroupFromFile(index)?.Name;
#endregion
}
}