using System.Collections.Generic; using System.IO; using SabreTools.Data.Models.InstallShieldArchiveV3; namespace SabreTools.Wrappers { /// /// Reference (de)compressor: https://www.sac.sk/download/pack/icomp95.zip /// /// public partial class InstallShieldArchiveV3 : WrapperBase { #region Descriptive Properties /// public override string DescriptionString => "InstallShield Archive V3"; #endregion #region Extension Properties /// public ushort DirCount => Model.Header.DirCount; /// public ushort FileCount => Model.Header.FileCount; /// public Data.Models.InstallShieldArchiveV3.Directory[] Directories => Model.Directories; /// public Data.Models.InstallShieldArchiveV3.File[] Files => Model.Files; /// /// Map of all files to their parent directories by index /// public Dictionary FileDirMap { get { // Return the prebuilt map if (field is not null) return field; // Build the file map field = []; int fileId = 0; for (int i = 0; i < Directories.Length; i++) { var dir = Directories[i]; for (int j = 0; j < dir.FileCount; j++) { field[fileId++] = i; } } return field; } } = null; /// /// Map of all files found in the archive /// public Dictionary FileNameMap { get { // Return the prebuilt map if (field is not null) return field; // Build the file map field = []; for (int fileIndex = 0; fileIndex < Files.Length; fileIndex++) { // Get the current file var file = Files[fileIndex]; // Get the parent directory int dirIndex = FileDirMap[fileIndex]; if (dirIndex < 0 || dirIndex >= DirCount) continue; // Create the filename string filename = Path.Combine( Directories[dirIndex].Name.Length == 0 ? $"dir_{dirIndex}" : Directories[dirIndex].Name, file.Name.Length == 0 ? $"file_{fileIndex}" : file.Name ); // Add to the map field[filename] = file; } return field; } } = null; /// /// Data offset for all archives /// private const uint DataStart = 255; #endregion #region Constructors /// public InstallShieldArchiveV3(Archive model, byte[] data) : base(model, data) { } /// public InstallShieldArchiveV3(Archive model, byte[] data, int offset) : base(model, data, offset) { } /// public InstallShieldArchiveV3(Archive model, byte[] data, int offset, int length) : base(model, data, offset, length) { } /// public InstallShieldArchiveV3(Archive model, Stream data) : base(model, data) { } /// public InstallShieldArchiveV3(Archive model, Stream data, long offset) : base(model, data, offset) { } /// public InstallShieldArchiveV3(Archive model, Stream data, long offset, long length) : base(model, data, offset, length) { } #endregion #region Static Constructors /// /// Create an InstallShield Archive V3 from a byte array and offset /// /// Byte array representing the archive /// Offset within the array to parse /// A archive wrapper on success, null on failure public static InstallShieldArchiveV3? 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); } /// /// Create a InstallShield Archive V3 from a Stream /// /// Stream representing the archive /// A archive wrapper on success, null on failure public static InstallShieldArchiveV3? 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.InstallShieldArchiveV3().Deserialize(data); if (model is null) return null; return new InstallShieldArchiveV3(model, data, currentOffset); } catch { return null; } } #endregion } }