diff --git a/BurnOutSharp/FileType/InstallShieldArchiveV3.cs b/BurnOutSharp/FileType/InstallShieldArchiveV3.cs
new file mode 100644
index 00000000..0c262097
--- /dev/null
+++ b/BurnOutSharp/FileType/InstallShieldArchiveV3.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Concurrent;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using UnshieldSharp.Archive;
+
+namespace BurnOutSharp.FileType
+{
+ internal class InstallShieldArchiveV3 : IScannable
+ {
+ ///
+ public bool ShouldScan(byte[] magic)
+ {
+ if (magic.StartsWith(new byte?[] { 0x13, 0x5D, 0x65, 0x8C }))
+ return true;
+
+ return false;
+ }
+
+ ///
+ public ConcurrentDictionary> Scan(Scanner scanner, string file)
+ {
+ if (!File.Exists(file))
+ return null;
+
+ using (var fs = File.OpenRead(file))
+ {
+ return Scan(scanner, fs, file);
+ }
+ }
+
+ // TODO: Add stream opening support
+ ///
+ public ConcurrentDictionary> Scan(Scanner scanner, Stream stream, string file)
+ {
+ // Get the name of the first cabinet file or header
+ string directory = Path.GetDirectoryName(file);
+ string noExtension = Path.GetFileNameWithoutExtension(file);
+ string filenamePattern = Path.Combine(directory, noExtension);
+ filenamePattern = new Regex(@"\d+$").Replace(filenamePattern, string.Empty);
+
+ bool cabinetHeaderExists = File.Exists(Path.Combine(directory, filenamePattern + "1.hdr"));
+ bool shouldScanCabinet = cabinetHeaderExists
+ ? file.Equals(Path.Combine(directory, filenamePattern + "1.hdr"), StringComparison.OrdinalIgnoreCase)
+ : file.Equals(Path.Combine(directory, filenamePattern + "1.cab"), StringComparison.OrdinalIgnoreCase);
+
+ // If we have the first file
+ if (shouldScanCabinet)
+ {
+ // If the cab file itself fails
+ try
+ {
+ string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
+ Directory.CreateDirectory(tempPath);
+
+ UnshieldSharp.Archive.InstallShieldArchiveV3 archive = new UnshieldSharp.Archive.InstallShieldArchiveV3(file);
+ foreach (CompressedFile cfile in archive.Files.Select(kvp => kvp.Value))
+ {
+ // If an individual entry fails
+ try
+ {
+ string tempFile = Path.Combine(tempPath, cfile.FullPath);
+ if (!Directory.Exists(Path.GetDirectoryName(tempFile)))
+ Directory.CreateDirectory(Path.GetDirectoryName(tempFile));
+
+ (byte[] fileContents, string error) = archive.Extract(cfile.FullPath);
+ if (!string.IsNullOrWhiteSpace(error))
+ continue;
+
+ using (FileStream fs = File.OpenWrite(tempFile))
+ {
+ fs.Write(fileContents, 0, fileContents.Length);
+ }
+ }
+ catch { }
+ }
+
+ // Collect and format all found protections
+ var protections = scanner.GetProtections(tempPath);
+
+ // If temp directory cleanup fails
+ try
+ {
+ Directory.Delete(tempPath, true);
+ }
+ catch { }
+
+ // Remove temporary path references
+ Utilities.StripFromKeys(protections, tempPath);
+
+ return protections;
+ }
+ catch { }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/BurnOutSharp/FileType/InstallShieldCAB.cs b/BurnOutSharp/FileType/InstallShieldCAB.cs
index 8d7ee841..4e91a1d5 100644
--- a/BurnOutSharp/FileType/InstallShieldCAB.cs
+++ b/BurnOutSharp/FileType/InstallShieldCAB.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Concurrent;
-using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using UnshieldSharp.Cabinet;
diff --git a/BurnOutSharp/Scanner.cs b/BurnOutSharp/Scanner.cs
index d2681e70..3d59dec4 100644
--- a/BurnOutSharp/Scanner.cs
+++ b/BurnOutSharp/Scanner.cs
@@ -320,6 +320,14 @@ namespace BurnOutSharp
Utilities.AppendToDictionary(protections, subProtections);
}
+ // InstallShield Archive V3 (Z)
+ if (file != null && new InstallShieldArchiveV3().ShouldScan(magic))
+ {
+ var subProtections = new InstallShieldArchiveV3().Scan(this, file);
+ Utilities.PrependToKeys(subProtections, file);
+ Utilities.AppendToDictionary(protections, subProtections);
+ }
+
// InstallShield Cabinet
if (file != null && new InstallShieldCAB().ShouldScan(magic))
{