From 5ea89eefe84cf7e7d1a778f21381fe4366e22955 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Tue, 10 Jan 2023 10:51:36 -0800 Subject: [PATCH] MSI was really CFB all along --- BurnOutSharp/Enums.cs | 10 +-- BurnOutSharp/FileType/{MSI.cs => CFB.cs} | 4 +- BurnOutSharp/Scanner.cs | 16 ++-- BurnOutSharp/Tools/Utilities.cs | 47 ++++++---- README.md | 3 +- Test/Extractor.cs | 104 +++++++++++------------ Test/Printer.cs | 38 ++++----- 7 files changed, 119 insertions(+), 103 deletions(-) rename BurnOutSharp/FileType/{MSI.cs => CFB.cs} (98%) diff --git a/BurnOutSharp/Enums.cs b/BurnOutSharp/Enums.cs index 74db8859..c861df19 100644 --- a/BurnOutSharp/Enums.cs +++ b/BurnOutSharp/Enums.cs @@ -25,6 +25,11 @@ /// BZip2, + /// + /// Compound File Binary + /// + CFB, + /// /// CTR Importable Archive /// @@ -80,11 +85,6 @@ /// MPQ, - /// - /// Microsoft installation package - /// - MSI, - /// /// Nintendo 3DS cart image /// diff --git a/BurnOutSharp/FileType/MSI.cs b/BurnOutSharp/FileType/CFB.cs similarity index 98% rename from BurnOutSharp/FileType/MSI.cs rename to BurnOutSharp/FileType/CFB.cs index b448c1be..a6b77b04 100644 --- a/BurnOutSharp/FileType/MSI.cs +++ b/BurnOutSharp/FileType/CFB.cs @@ -9,9 +9,9 @@ using static BurnOutSharp.Utilities.Dictionary; namespace BurnOutSharp.FileType { /// - /// Microsoft installation package + /// Compound File Binary /// - public class MSI : IScannable + public class CFB : IScannable { /// public ConcurrentDictionary> Scan(Scanner scanner, string file) diff --git a/BurnOutSharp/Scanner.cs b/BurnOutSharp/Scanner.cs index d399224a..cadff05c 100644 --- a/BurnOutSharp/Scanner.cs +++ b/BurnOutSharp/Scanner.cs @@ -427,6 +427,14 @@ namespace BurnOutSharp AppendToDictionary(protections, subProtections); } + // CFB + if (fileName != null && scannable is CFB) + { + var subProtections = scannable.Scan(this, fileName); + PrependToKeys(subProtections, fileName); + AppendToDictionary(protections, subProtections); + } + // GCF if (scannable is GCF) { @@ -475,14 +483,6 @@ namespace BurnOutSharp AppendToDictionary(protections, subProtections); } - // MSI - if (fileName != null && scannable is MSI) - { - var subProtections = scannable.Scan(this, fileName); - PrependToKeys(subProtections, fileName); - AppendToDictionary(protections, subProtections); - } - // MoPaQ archive if (fileName != null && scannable is MPQ) { diff --git a/BurnOutSharp/Tools/Utilities.cs b/BurnOutSharp/Tools/Utilities.cs index 3ba036d1..f90a0aae 100644 --- a/BurnOutSharp/Tools/Utilities.cs +++ b/BurnOutSharp/Tools/Utilities.cs @@ -43,6 +43,13 @@ namespace BurnOutSharp.Tools #endregion + #region CFB + + if (magic.StartsWith(new byte?[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 })) + return SupportedFileType.CFB; + + #endregion + #region CIA // No magic checks for CIA @@ -150,13 +157,6 @@ namespace BurnOutSharp.Tools #endregion - #region MSI - - if (magic.StartsWith(new byte?[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 })) - return SupportedFileType.MSI; - - #endregion - #region N3DS // No magic checks for N3DS @@ -365,6 +365,30 @@ namespace BurnOutSharp.Tools #endregion + #region CFB + + // Installer package + if (extension.Equals("msi", StringComparison.OrdinalIgnoreCase)) + return SupportedFileType.CFB; + + // Merge module + else if (extension.Equals("msm", StringComparison.OrdinalIgnoreCase)) + return SupportedFileType.CFB; + + // Patch Package + else if (extension.Equals("msp", StringComparison.OrdinalIgnoreCase)) + return SupportedFileType.CFB; + + // Transform + else if (extension.Equals("mst", StringComparison.OrdinalIgnoreCase)) + return SupportedFileType.CFB; + + // Patch Creation Properties + else if (extension.Equals("pcp", StringComparison.OrdinalIgnoreCase)) + return SupportedFileType.CFB; + + #endregion + #region CIA if (extension.Equals("cia", StringComparison.OrdinalIgnoreCase)) @@ -433,13 +457,6 @@ namespace BurnOutSharp.Tools #endregion - #region MSI - - if (extension.Equals("msi", StringComparison.OrdinalIgnoreCase)) - return SupportedFileType.MSI; - - #endregion - #region N3DS // 3DS cart image @@ -717,6 +734,7 @@ namespace BurnOutSharp.Tools case SupportedFileType.BFPK: return new FileType.BFPK(); case SupportedFileType.BSP: return new FileType.BSP(); case SupportedFileType.BZip2: return new FileType.BZip2(); + case SupportedFileType.CFB: return new FileType.CFB(); //case SupportedFileType.CIA: return new FileType.CIA(); case SupportedFileType.Executable: return new FileType.Executable(); case SupportedFileType.GCF: return new FileType.GCF(); @@ -728,7 +746,6 @@ namespace BurnOutSharp.Tools case SupportedFileType.MicrosoftCAB: return new FileType.MicrosoftCAB(); case SupportedFileType.MicrosoftLZ: return new FileType.MicrosoftLZ(); case SupportedFileType.MPQ: return new FileType.MPQ(); - case SupportedFileType.MSI: return new FileType.MSI(); //case SupportedFileType.N3DS: return new FileType.N3DS(); //case SupportedFileType.NCF: return new FileType.NCF(); //case SupportedFileType.Nitro: return new FileType.Nitro(); diff --git a/README.md b/README.md index dbe775f4..56c04e99 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ Below is a list of container formats that are supported in some way: | 7-zip archive | No | Yes | Yes | Via `SharpCompress` | | BFPK custom archive format | Yes | Yes | Yes | | | bzip2 archive | No | Yes | Yes | Via `SharpCompress` | -| Compound File Binary (CFB) | Yes | Yes* | No | Basis of MSI, only CFB common pieces printable | +| Compound File Binary (CFB) | Yes* | Yes | Yes | Via `OpenMcdf`, only CFB common pieces printable | | gzip archive | No | Yes | Yes | Via `SharpCompress` | | Half-Life Game Cache File (GCF) | Yes | Yes | Yes | | | Half-Life Level (BSP) | Yes | Yes | Yes | | @@ -171,7 +171,6 @@ Below is a list of container formats that are supported in some way: | Microsoft cabinet file | Yes | Yes | Yes | | | Microsoft LZ-compressed files | No | Yes | Yes | | | MoPaQ game data archive (MPQ) | No | Yes | Yes | Via `StormLibSharp` | -| Microsoft installation package (MSI) | No | Yes | Yes | Via `OpenMcdf` | | MS-DOS Executable | Yes | Yes | No | Incomplete | | New Exectuable | Yes | Yes | No | Incomplete | | Nintendo 3DS cart image | Yes | Yes | No | | diff --git a/Test/Extractor.cs b/Test/Extractor.cs index aa304718..f5dc3c51 100644 --- a/Test/Extractor.cs +++ b/Test/Extractor.cs @@ -189,6 +189,58 @@ namespace Test } } + // CFB + else if (ft == SupportedFileType.CFB) + { + // Build the installer information + Console.WriteLine("Extracting CFB contents"); + Console.WriteLine(); + + // If the CFB file itself fails + try + { + using (CompoundFile cf = new CompoundFile(stream, CFSUpdateMode.ReadOnly, CFSConfiguration.Default)) + { + cf.RootStorage.VisitEntries((e) => + { + if (!e.IsStream) + return; + + var str = cf.RootStorage.GetStream(e.Name); + if (str == null) + return; + + byte[] strData = str.GetData(); + if (strData == null) + return; + + string decoded = BurnOutSharp.FileType.CFB.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(outputDirectory, decoded); + using (Stream fs = File.OpenWrite(filename)) + { + fs.Write(strData, 0, strData.Length); + } + }, recursive: true); + } + } + catch (Exception ex) + { + Console.WriteLine($"Something went wrong extracting CFB: {ex}"); + Console.WriteLine(); + } + } + // GCF else if (ft == SupportedFileType.GCF) { @@ -458,58 +510,6 @@ namespace Test } #endif - // MSI - else if (ft == SupportedFileType.MSI) - { - // Build the installer information - Console.WriteLine("Extracting MSI contents"); - Console.WriteLine(); - - // If the MSI file itself fails - try - { - 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 = BurnOutSharp.FileType.MSI.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(outputDirectory, decoded); - using (Stream fs = File.OpenWrite(filename)) - { - fs.Write(strData, 0, strData.Length); - } - }, recursive: true); - } - } - catch (Exception ex) - { - Console.WriteLine($"Something went wrong extracting MSI: {ex}"); - Console.WriteLine(); - } - } - // PAK else if (ft == SupportedFileType.PAK) { diff --git a/Test/Printer.cs b/Test/Printer.cs index bbf58165..4ffc4184 100644 --- a/Test/Printer.cs +++ b/Test/Printer.cs @@ -174,6 +174,25 @@ namespace Test bsp.Print(); } + // CFB + else if (ft == SupportedFileType.CFB) + { + // Build the CFB information + Console.WriteLine("Creating Compact File Binary deserializer"); + Console.WriteLine(); + + var cfb = CFB.Create(stream); + if (cfb == null) + { + Console.WriteLine("Something went wrong parsing Compact File Binary"); + Console.WriteLine(); + return; + } + + // Print the CFB to screen + cfb.Print(); + } + // CIA else if (ft == SupportedFileType.CIA) { @@ -257,25 +276,6 @@ namespace Test cabinet.Print(); } - // MSI -- TODO: Technically CFB - else if (ft == SupportedFileType.MSI) - { - // Build the CFB information - Console.WriteLine("Creating Compact File Binary deserializer"); - Console.WriteLine(); - - var cfb = CFB.Create(stream); - if (cfb == null) - { - Console.WriteLine("Something went wrong parsing Compact File Binary"); - Console.WriteLine(); - return; - } - - // Print the CFB to screen - cfb.Print(); - } - // N3DS else if (ft == SupportedFileType.N3DS) {