using System.Collections.Generic; using System.IO; using SabreTools.Data.Models.InstallShieldExecutable; using SabreTools.IO.Extensions; using SabreTools.Text.Extensions; using static SabreTools.Data.Models.InstallShieldExecutable.Constants; #pragma warning disable IDE0017 // Simplify object initialization namespace SabreTools.Serialization.Readers { public class InstallShieldExecutable : BaseBinaryReader { public override SFX? Deserialize(Stream? data) { // If the data is invalid if (data is null || !data.CanRead) return null; try { var sfx = new SFX(); // Cache the initial offset long initialOffset = data.Position; var sfxList = new List(); while (data.Position < data.Length) { // Try to parse the entry var fileEntry = ParseFileEntry(data, initialOffset); if (fileEntry is null) break; // Get the length, and make sure it won't EOF long length = (long)fileEntry.Length; if (length > data.Length - data.Position) break; data.SeekIfPossible(length, SeekOrigin.Current); sfxList.Add(fileEntry); } if (sfxList.Count == 0) return null; sfx.Entries = [.. sfxList]; return sfx; } catch { // Ignore the actual error return null; } } /// /// Parse a Stream into a FileEntry /// /// Stream to parse /// Filled FileEntry on success, null on error public static FileEntry? ParseFileEntry(Stream data, long initialOffset) { string? name = data.ReadNullTerminatedAnsiString(); if (name is null) return null; // Both of these strings indicate that this is a different kind of encrypted and/or compressed format of // ISEXE that is not yet supported, but will be in the future. // They return early because no extraction can be performed, like how MsCab currently returns if a folder // is LZX or Quantum. if (name == ISSignatureString) return null; if (name == ISSetupSignatureString) return null; string? path = data.ReadNullTerminatedAnsiString(); if (path is null) return null; string? version = data.ReadNullTerminatedAnsiString(); if (version is null) return null; var lengthString = data.ReadNullTerminatedAnsiString(); if (lengthString is null || !ulong.TryParse(lengthString, out var lengthValue)) return null; var obj = new FileEntry(); obj.Name = name; obj.Path = path; obj.Version = version; obj.Length = lengthValue; obj.Offset = data.Position - initialOffset; return obj; } } }