diff --git a/BinaryObjectScanner.Interfaces/IExtractable.cs b/BinaryObjectScanner.Interfaces/IExtractable.cs index c038f967..53f83e45 100644 --- a/BinaryObjectScanner.Interfaces/IExtractable.cs +++ b/BinaryObjectScanner.Interfaces/IExtractable.cs @@ -12,7 +12,10 @@ namespace BinaryObjectScanner.Interfaces /// /// Path to the input file /// Path to extracted files, null on error - /// Ideally, this should just point to the other extract implementation + /// + /// Ideally, this should just point to the other extract implementation. + /// It is expected that the calling method will provide exception handling. + /// string Extract(string file); /// @@ -21,6 +24,7 @@ namespace BinaryObjectScanner.Interfaces /// Stream representing the input file /// Path to the input file /// Path to extracted files, null on error + /// It is expected that the calling method will provide exception handling. string Extract(Stream stream, string file); } } diff --git a/BurnOutSharp/FileType/BFPK.cs b/BurnOutSharp/FileType/BFPK.cs index 41666c92..526a04be 100644 --- a/BurnOutSharp/FileType/BFPK.cs +++ b/BurnOutSharp/FileType/BFPK.cs @@ -27,8 +27,19 @@ namespace BurnOutSharp.FileType /// public string Extract(Stream stream, string file) { - // Implement from existing Scan - return null; + // Create the wrapper + BinaryObjectScanner.Wrappers.BFPK bfpk = BinaryObjectScanner.Wrappers.BFPK.Create(stream); + if (bfpk == null) + return null; + + // Create a temp output directory + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + // Extract all files + bfpk.ExtractAll(tempPath); + + return tempPath; } /// diff --git a/BurnOutSharp/FileType/BSP.cs b/BurnOutSharp/FileType/BSP.cs index d23c0fa9..dc6bb0bc 100644 --- a/BurnOutSharp/FileType/BSP.cs +++ b/BurnOutSharp/FileType/BSP.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Concurrent; using System.IO; using BurnOutSharp.Interfaces; +using BinaryObjectScanner.Interfaces; using static BinaryObjectScanner.Utilities.Dictionary; namespace BurnOutSharp.FileType @@ -9,8 +10,39 @@ namespace BurnOutSharp.FileType /// /// Half-Life Level /// - public class BSP : IScannable + public class BSP : IExtractable, IScannable { + /// + public string Extract(string file) + { + if (!File.Exists(file)) + return null; + + using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + return Extract(fs, file); + } + } + + /// + public string Extract(Stream stream, string file) + { + // Create the wrapper + BinaryObjectScanner.Wrappers.BSP bsp = BinaryObjectScanner.Wrappers.BSP.Create(stream); + if (bsp == null) + return null; + + // Create a temp output directory + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + // Loop through and extract all files + bsp.ExtractAllLumps(tempPath); + bsp.ExtractAllTextures(tempPath); + + return tempPath; + } + /// public ConcurrentDictionary> Scan(Scanner scanner, string file) { diff --git a/BurnOutSharp/FileType/BZip2.cs b/BurnOutSharp/FileType/BZip2.cs index 1c42000d..e459467b 100644 --- a/BurnOutSharp/FileType/BZip2.cs +++ b/BurnOutSharp/FileType/BZip2.cs @@ -29,8 +29,20 @@ namespace BurnOutSharp.FileType /// public string Extract(Stream stream, string file) { - // Implement from existing Scan - return null; + // Create a temp output directory + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + using (BZip2Stream bz2File = new BZip2Stream(stream, CompressionMode.Decompress, true)) + { + string tempFile = Path.Combine(tempPath, Guid.NewGuid().ToString()); + using (FileStream fs = File.OpenWrite(tempFile)) + { + bz2File.CopyTo(fs); + } + } + + return tempPath; } /// diff --git a/BurnOutSharp/FileType/CFB.cs b/BurnOutSharp/FileType/CFB.cs index 44388725..08c72f7d 100644 --- a/BurnOutSharp/FileType/CFB.cs +++ b/BurnOutSharp/FileType/CFB.cs @@ -29,8 +29,46 @@ namespace BurnOutSharp.FileType /// public string Extract(Stream stream, string file) { - // Implement from existing Scan - return null; + // Create a temp output directory + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + using (CompoundFile msi = new CompoundFile(stream, CFSUpdateMode.ReadOnly, CFSConfiguration.Default)) + { + msi.RootStorage.VisitEntries((e) => + { + if (!e.IsStream) + return; + + var str = msi.RootStorage.GetStream(e.Name); + if (str == null) + return; + + byte[] strData = str.GetData(); + if (strData == null) + return; + + string decoded = DecodeStreamName(e.Name).TrimEnd('\0'); + byte[] nameBytes = Encoding.UTF8.GetBytes(e.Name); + + // UTF-8 encoding of 0x4840. + if (nameBytes[0] == 0xe4 && nameBytes[1] == 0xa1 && nameBytes[2] == 0x80) + decoded = decoded.Substring(3); + + foreach (char c in Path.GetInvalidFileNameChars()) + { + decoded = decoded.Replace(c, '_'); + } + + string filename = Path.Combine(tempPath, decoded); + using (Stream fs = File.OpenWrite(filename)) + { + fs.Write(strData, 0, strData.Length); + } + }, recursive: true); + } + + return tempPath; } /// diff --git a/BurnOutSharp/FileType/GCF.cs b/BurnOutSharp/FileType/GCF.cs index 14bfdbca..7343574c 100644 --- a/BurnOutSharp/FileType/GCF.cs +++ b/BurnOutSharp/FileType/GCF.cs @@ -27,8 +27,19 @@ namespace BurnOutSharp.FileType /// public string Extract(Stream stream, string file) { - // Implement from existing Scan - return null; + // Create the wrapper + BinaryObjectScanner.Wrappers.GCF gcf = BinaryObjectScanner.Wrappers.GCF.Create(stream); + if (gcf == null) + return null; + + // Create a temp output directory + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + // Loop through and extract all files + gcf.ExtractAll(tempPath); + + return tempPath; } /// diff --git a/BurnOutSharp/FileType/GZIP.cs b/BurnOutSharp/FileType/GZIP.cs index 43b5cef1..fb0a7c41 100644 --- a/BurnOutSharp/FileType/GZIP.cs +++ b/BurnOutSharp/FileType/GZIP.cs @@ -29,8 +29,24 @@ namespace BurnOutSharp.FileType /// public string Extract(Stream stream, string file) { - // Implement from existing Scan - return null; + // Create a temp output directory + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + using (GZipArchive zipFile = GZipArchive.Open(stream)) + { + foreach (var entry in zipFile.Entries) + { + // If we have a directory, skip it + if (entry.IsDirectory) + continue; + + string tempFile = Path.Combine(tempPath, entry.Key); + entry.WriteToFile(tempFile); + } + } + + return tempPath; } /// diff --git a/BurnOutSharp/FileType/InstallShieldArchiveV3.cs b/BurnOutSharp/FileType/InstallShieldArchiveV3.cs index fc4a3d2f..fe1d0cd8 100644 --- a/BurnOutSharp/FileType/InstallShieldArchiveV3.cs +++ b/BurnOutSharp/FileType/InstallShieldArchiveV3.cs @@ -29,8 +29,28 @@ namespace BurnOutSharp.FileType /// public string Extract(Stream stream, string file) { - // Implement from existing Scan - return null; + // Create a temp output directory + 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)) + { + 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); + } + } + + return tempPath; } /// diff --git a/BurnOutSharp/FileType/InstallShieldCAB.cs b/BurnOutSharp/FileType/InstallShieldCAB.cs index 04249cd2..8479ce4e 100644 --- a/BurnOutSharp/FileType/InstallShieldCAB.cs +++ b/BurnOutSharp/FileType/InstallShieldCAB.cs @@ -29,8 +29,32 @@ namespace BurnOutSharp.FileType /// public string Extract(Stream stream, string file) { - // Implement from existing Scan - return null; + // Create a temp output directory + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + InstallShieldCabinet cabfile = InstallShieldCabinet.Open(file); + for (int i = 0; i < cabfile.FileCount; i++) + { + // Check if the file is valid first + if (!cabfile.FileIsValid(i)) + continue; + + string tempFile; + try + { + string filename = cabfile.FileName(i); + tempFile = Path.Combine(tempPath, filename); + } + catch + { + tempFile = Path.Combine(tempPath, $"BAD_FILENAME{i}"); + } + + cabfile.FileSave(i, tempFile); + } + + return tempPath; } /// diff --git a/BurnOutSharp/FileType/MPQ.cs b/BurnOutSharp/FileType/MPQ.cs index 4c625f00..4ac0b90d 100644 --- a/BurnOutSharp/FileType/MPQ.cs +++ b/BurnOutSharp/FileType/MPQ.cs @@ -27,11 +27,48 @@ namespace BurnOutSharp.FileType } } + // TODO: Add stream opening support /// public string Extract(Stream stream, string file) { - // Implement from existing Scan +#if NET6_0_OR_GREATER + // Not supported for .NET 6.0 due to Windows DLL requirements return null; +#else + // Create a temp output directory + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + using (MpqArchive mpqArchive = new MpqArchive(file, FileAccess.Read)) + { + // Try to open the listfile + string listfile = null; + MpqFileStream listStream = mpqArchive.OpenFile("(listfile)"); + + // If we can't read the listfile, we just return + if (!listStream.CanRead) + return null; + + // Read the listfile in for processing + using (StreamReader sr = new StreamReader(listStream)) + { + listfile = sr.ReadToEnd(); + } + + // Split the listfile by newlines + string[] listfileLines = listfile.Replace("\r\n", "\n").Split('\n'); + + // Loop over each entry + foreach (string sub in listfileLines) + { + string tempFile = Path.Combine(tempPath, sub); + Directory.CreateDirectory(Path.GetDirectoryName(tempFile)); + mpqArchive.ExtractFile(sub, tempFile); + } + } + + return tempPath; +#endif } /// diff --git a/BurnOutSharp/FileType/MicrosoftCAB.cs b/BurnOutSharp/FileType/MicrosoftCAB.cs index 5dc50b40..3f8d5606 100644 --- a/BurnOutSharp/FileType/MicrosoftCAB.cs +++ b/BurnOutSharp/FileType/MicrosoftCAB.cs @@ -30,8 +30,21 @@ namespace BurnOutSharp.FileType /// public string Extract(Stream stream, string file) { - // Implement from existing Scan - return null; + // Open the cab file + var cabFile = MicrosoftCabinet.Create(stream); + if (cabFile == null) + return null; + + // Create a temp output directory + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + // If entry extraction fails + bool success = cabFile.ExtractAll(tempPath); + if (!success) + return null; + + return tempPath; } /// diff --git a/BurnOutSharp/FileType/MicrosoftLZ.cs b/BurnOutSharp/FileType/MicrosoftLZ.cs index 1e5b18df..0438fd0f 100644 --- a/BurnOutSharp/FileType/MicrosoftLZ.cs +++ b/BurnOutSharp/FileType/MicrosoftLZ.cs @@ -29,8 +29,33 @@ namespace BurnOutSharp.FileType /// public string Extract(Stream stream, string file) { - // Implement from existing Scan - return null; + // Create a temp output directory + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + byte[] data = LZ.Decompress(stream); + + // Create the temp filename + string tempFile = "temp.bin"; + if (!string.IsNullOrEmpty(file)) + { + string expandedFilePath = LZ.GetExpandedName(file, out _); + tempFile = Path.GetFileName(expandedFilePath).TrimEnd('\0'); + if (tempFile.EndsWith(".ex")) + tempFile += "e"; + else if (tempFile.EndsWith(".dl")) + tempFile += "l"; + } + + tempFile = Path.Combine(tempPath, tempFile); + + // Write the file data to a temp file + using (Stream tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) + { + tempStream.Write(data, 0, data.Length); + } + + return tempPath; } /// diff --git a/BurnOutSharp/FileType/PAK.cs b/BurnOutSharp/FileType/PAK.cs index f5e4040e..6002d30d 100644 --- a/BurnOutSharp/FileType/PAK.cs +++ b/BurnOutSharp/FileType/PAK.cs @@ -27,8 +27,19 @@ namespace BurnOutSharp.FileType /// public string Extract(Stream stream, string file) { - // Implement from existing Scan - return null; + // Create the wrapper + BinaryObjectScanner.Wrappers.PAK pak = BinaryObjectScanner.Wrappers.PAK.Create(stream); + if (pak == null) + return null; + + // Create a temp output directory + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + // Loop through and extract all files + pak.ExtractAll(tempPath); + + return tempPath; } /// diff --git a/BurnOutSharp/FileType/PFF.cs b/BurnOutSharp/FileType/PFF.cs index 31d80fea..b7f46205 100644 --- a/BurnOutSharp/FileType/PFF.cs +++ b/BurnOutSharp/FileType/PFF.cs @@ -27,8 +27,19 @@ namespace BurnOutSharp.FileType /// public string Extract(Stream stream, string file) { - // Implement from existing Scan - return null; + // Create the wrapper + BinaryObjectScanner.Wrappers.PFF pff = BinaryObjectScanner.Wrappers.PFF.Create(stream); + if (pff == null) + return null; + + // Create a temp output directory + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + // Extract all files + pff.ExtractAll(tempPath); + + return tempPath; } /// diff --git a/BurnOutSharp/FileType/PKZIP.cs b/BurnOutSharp/FileType/PKZIP.cs index b4137f93..542a90c6 100644 --- a/BurnOutSharp/FileType/PKZIP.cs +++ b/BurnOutSharp/FileType/PKZIP.cs @@ -29,8 +29,25 @@ namespace BurnOutSharp.FileType /// public string Extract(Stream stream, string file) { - // Implement from existing Scan - return null; + // Create a temp output directory + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + using (ZipArchive zipFile = ZipArchive.Open(stream)) + { + foreach (var entry in zipFile.Entries) + { + // If we have a directory, skip it + if (entry.IsDirectory) + continue; + + string tempFile = Path.Combine(tempPath, entry.Key); + Directory.CreateDirectory(Path.GetDirectoryName(tempFile)); + entry.WriteToFile(tempFile); + } + } + + return tempPath; } /// diff --git a/BurnOutSharp/FileType/RAR.cs b/BurnOutSharp/FileType/RAR.cs index 9eb203e0..aa558577 100644 --- a/BurnOutSharp/FileType/RAR.cs +++ b/BurnOutSharp/FileType/RAR.cs @@ -29,8 +29,24 @@ namespace BurnOutSharp.FileType /// public string Extract(Stream stream, string file) { - // Implement from existing Scan - return null; + // Create a temp output directory + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + using (RarArchive rarFile = RarArchive.Open(stream)) + { + foreach (var entry in rarFile.Entries) + { + // If we have a directory, skip it + if (entry.IsDirectory) + continue; + + string tempFile = Path.Combine(tempPath, entry.Key); + entry.WriteToFile(tempFile); + } + } + + return tempPath; } /// diff --git a/BurnOutSharp/FileType/SFFS.cs b/BurnOutSharp/FileType/SFFS.cs index d05b6c65..28107810 100644 --- a/BurnOutSharp/FileType/SFFS.cs +++ b/BurnOutSharp/FileType/SFFS.cs @@ -28,7 +28,6 @@ namespace BurnOutSharp.FileType /// public string Extract(Stream stream, string file) { - // Implement from existing Scan return null; } diff --git a/BurnOutSharp/FileType/SGA.cs b/BurnOutSharp/FileType/SGA.cs index c92cb6ba..45e95502 100644 --- a/BurnOutSharp/FileType/SGA.cs +++ b/BurnOutSharp/FileType/SGA.cs @@ -27,8 +27,19 @@ namespace BurnOutSharp.FileType /// public string Extract(Stream stream, string file) { - // Implement from existing Scan - return null; + // Create the wrapper + BinaryObjectScanner.Wrappers.SGA sga = BinaryObjectScanner.Wrappers.SGA.Create(stream); + if (sga == null) + return null; + + // Create a temp output directory + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + // Loop through and extract all files + sga.ExtractAll(tempPath); + + return tempPath; } /// diff --git a/BurnOutSharp/FileType/SevenZip.cs b/BurnOutSharp/FileType/SevenZip.cs index 0455a1d3..abafb158 100644 --- a/BurnOutSharp/FileType/SevenZip.cs +++ b/BurnOutSharp/FileType/SevenZip.cs @@ -29,8 +29,24 @@ namespace BurnOutSharp.FileType /// public string Extract(Stream stream, string file) { - // Implement from existing Scan - return null; + // Create a temp output directory + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + using (SevenZipArchive sevenZipFile = SevenZipArchive.Open(stream)) + { + foreach (var entry in sevenZipFile.Entries) + { + // If we have a directory, skip it + if (entry.IsDirectory) + continue; + + string tempFile = Path.Combine(tempPath, entry.Key); + entry.WriteToFile(tempFile); + } + } + + return tempPath; } /// diff --git a/BurnOutSharp/FileType/TapeArchive.cs b/BurnOutSharp/FileType/TapeArchive.cs index dbd312db..b48ed8a7 100644 --- a/BurnOutSharp/FileType/TapeArchive.cs +++ b/BurnOutSharp/FileType/TapeArchive.cs @@ -29,8 +29,24 @@ namespace BurnOutSharp.FileType /// public string Extract(Stream stream, string file) { - // Implement from existing Scan - return null; + // Create a temp output directory + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + using (TarArchive tarFile = TarArchive.Open(stream)) + { + foreach (var entry in tarFile.Entries) + { + // If we have a directory, skip it + if (entry.IsDirectory) + continue; + + string tempFile = Path.Combine(tempPath, entry.Key); + entry.WriteToFile(tempFile); + } + } + + return tempPath; } /// diff --git a/BurnOutSharp/FileType/VBSP.cs b/BurnOutSharp/FileType/VBSP.cs index 9e76c43f..a2dca8f8 100644 --- a/BurnOutSharp/FileType/VBSP.cs +++ b/BurnOutSharp/FileType/VBSP.cs @@ -27,8 +27,19 @@ namespace BurnOutSharp.FileType /// public string Extract(Stream stream, string file) { - // Implement from existing Scan - return null; + // Create the wrapper + BinaryObjectScanner.Wrappers.VBSP vbsp = BinaryObjectScanner.Wrappers.VBSP.Create(stream); + if (vbsp == null) + return null; + + // Create a temp output directory + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + // Loop through and extract all files + vbsp.ExtractAllLumps(tempPath); + + return tempPath; } /// diff --git a/BurnOutSharp/FileType/VPK.cs b/BurnOutSharp/FileType/VPK.cs index 6071a909..e87c6881 100644 --- a/BurnOutSharp/FileType/VPK.cs +++ b/BurnOutSharp/FileType/VPK.cs @@ -27,8 +27,19 @@ namespace BurnOutSharp.FileType /// public string Extract(Stream stream, string file) { - // Implement from existing Scan - return null; + // Create the wrapper + BinaryObjectScanner.Wrappers.VPK vpk = BinaryObjectScanner.Wrappers.VPK.Create(stream); + if (vpk == null) + return null; + + // Create a temp output directory + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + // Loop through and extract all files + vpk.ExtractAll(tempPath); + + return tempPath; } /// diff --git a/BurnOutSharp/FileType/WAD.cs b/BurnOutSharp/FileType/WAD.cs index e147b207..4f1a9d66 100644 --- a/BurnOutSharp/FileType/WAD.cs +++ b/BurnOutSharp/FileType/WAD.cs @@ -27,8 +27,19 @@ namespace BurnOutSharp.FileType /// public string Extract(Stream stream, string file) { - // Implement from existing Scan - return null; + // Create the wrapper + BinaryObjectScanner.Wrappers.WAD wad = BinaryObjectScanner.Wrappers.WAD.Create(stream); + if (wad == null) + return null; + + // Create a temp output directory + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + // Loop through and extract all files + wad.ExtractAllLumps(tempPath); + + return tempPath; } /// diff --git a/BurnOutSharp/FileType/XZ.cs b/BurnOutSharp/FileType/XZ.cs index aad94596..6f973b57 100644 --- a/BurnOutSharp/FileType/XZ.cs +++ b/BurnOutSharp/FileType/XZ.cs @@ -28,8 +28,20 @@ namespace BurnOutSharp.FileType /// public string Extract(Stream stream, string file) { - // Implement from existing Scan - return null; + // Create a temp output directory + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + using (XZStream xzFile = new XZStream(stream)) + { + string tempFile = Path.Combine(tempPath, Guid.NewGuid().ToString()); + using (FileStream fs = File.OpenWrite(tempFile)) + { + xzFile.CopyTo(fs); + } + } + + return tempPath; } /// diff --git a/BurnOutSharp/FileType/XZP.cs b/BurnOutSharp/FileType/XZP.cs index 3852d5d3..7c1b7de1 100644 --- a/BurnOutSharp/FileType/XZP.cs +++ b/BurnOutSharp/FileType/XZP.cs @@ -27,8 +27,19 @@ namespace BurnOutSharp.FileType /// public string Extract(Stream stream, string file) { - // Implement from existing Scan - return null; + // Create the wrapper + BinaryObjectScanner.Wrappers.XZP xzp = BinaryObjectScanner.Wrappers.XZP.Create(stream); + if (xzp == null) + return null; + + // Create a temp output directory + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + // Loop through and extract all files + xzp.ExtractAll(tempPath); + + return tempPath; } ///