diff --git a/BinaryObjectScanner/BinaryObjectScanner.csproj b/BinaryObjectScanner/BinaryObjectScanner.csproj index c08d5dda..8838c5de 100644 --- a/BinaryObjectScanner/BinaryObjectScanner.csproj +++ b/BinaryObjectScanner/BinaryObjectScanner.csproj @@ -96,8 +96,8 @@ - - + + diff --git a/BinaryObjectScanner/Factory.cs b/BinaryObjectScanner/Factory.cs index 4c70ae60..a42372f9 100644 --- a/BinaryObjectScanner/Factory.cs +++ b/BinaryObjectScanner/Factory.cs @@ -62,7 +62,7 @@ namespace BinaryObjectScanner WrapperType.TapeArchive => new FileType.TapeArchive(), WrapperType.VBSP => new FileType.VBSP(), WrapperType.VPK => new FileType.VPK(), - WrapperType.WAD => new FileType.WAD(), + WrapperType.WAD => new FileType.WAD3(), WrapperType.XZ => new FileType.XZ(), WrapperType.XZP => new FileType.XZP(), _ => null, diff --git a/BinaryObjectScanner/FileType/BSP.cs b/BinaryObjectScanner/FileType/BSP.cs index 425c0cb6..c829d4bd 100644 --- a/BinaryObjectScanner/FileType/BSP.cs +++ b/BinaryObjectScanner/FileType/BSP.cs @@ -1,7 +1,7 @@ using System; -using System.Collections.Generic; using System.IO; using BinaryObjectScanner.Interfaces; +using SabreTools.Models.BSP; namespace BinaryObjectScanner.FileType { @@ -30,10 +30,11 @@ namespace BinaryObjectScanner.FileType if (bsp == null) return false; + // TODO: Introduce helper methods for all specialty lump types + // Loop through and extract all files Directory.CreateDirectory(outDir); ExtractAllLumps(bsp, outDir); - ExtractAllTextures(bsp, outDir); return true; } @@ -52,12 +53,12 @@ namespace BinaryObjectScanner.FileType public static bool ExtractAllLumps(SabreTools.Serialization.Wrappers.BSP item, string outputDirectory) { // If we have no lumps - if (item.Model.Lumps == null || item.Model.Lumps.Length == 0) + if (item.Model.Header?.Lumps == null || item.Model.Header.Lumps.Length == 0) return false; // Loop through and extract all lumps to the output bool allExtracted = true; - for (int i = 0; i < item.Model.Lumps.Length; i++) + for (int i = 0; i < item.Model.Header.Lumps.Length; i++) { allExtracted &= ExtractLump(item, i, outputDirectory); } @@ -74,15 +75,15 @@ namespace BinaryObjectScanner.FileType public static bool ExtractLump(SabreTools.Serialization.Wrappers.BSP item, int index, string outputDirectory) { // If we have no lumps - if (item.Model.Lumps == null || item.Model.Lumps.Length == 0) + if (item.Model.Header?.Lumps == null || item.Model.Header.Lumps.Length == 0) return false; // If the lumps index is invalid - if (index < 0 || index >= item.Model.Lumps.Length) + if (index < 0 || index >= item.Model.Header.Lumps.Length) return false; // Get the lump - var lump = item.Model.Lumps[index]; + var lump = item.Model.Header.Lumps[index]; if (lump == null) return false; @@ -93,12 +94,12 @@ namespace BinaryObjectScanner.FileType // Create the filename string filename = $"lump_{index}.bin"; - switch (index) + switch ((LumpType)index) { - case SabreTools.Models.BSP.Constants.HL_BSP_LUMP_ENTITIES: + case LumpType.LUMP_ENTITIES: filename = "entities.ent"; break; - case SabreTools.Models.BSP.Constants.HL_BSP_LUMP_TEXTUREDATA: + case LumpType.LUMP_TEXTURES: filename = "texture_data.bin"; break; } @@ -119,10 +120,8 @@ namespace BinaryObjectScanner.FileType try { // Open the output file for writing - using (Stream fs = File.OpenWrite(filename)) - { - fs.Write(data, 0, data.Length); - } + using Stream fs = File.OpenWrite(filename); + fs.Write(data, 0, data.Length); } catch { @@ -131,172 +130,5 @@ namespace BinaryObjectScanner.FileType return true; } - - /// - /// Extract all textures from the BSP to an output directory - /// - /// Output directory to write to - /// True if all textures extracted, false otherwise - public static bool ExtractAllTextures(SabreTools.Serialization.Wrappers.BSP item, string outputDirectory) - { - // If we have no textures - if (item.Model.TextureHeader?.Offsets == null || item.Model.TextureHeader.Offsets.Length == 0) - return false; - - // Loop through and extract all lumps to the output - bool allExtracted = true; - for (int i = 0; i < item.Model.TextureHeader.Offsets.Length; i++) - { - allExtracted &= ExtractTexture(item, i, outputDirectory); - } - - return allExtracted; - } - - /// - /// Extract a texture from the BSP to an output directory by index - /// - /// Lump index to extract - /// Output directory to write to - /// True if the texture extracted, false otherwise - public static bool ExtractTexture(SabreTools.Serialization.Wrappers.BSP item, int index, string outputDirectory) - { - // If we have no textures - if (item.Model.Textures == null || item.Model.Textures.Length == 0) - return false; - - // If the texture index is invalid - if (index < 0 || index >= item.Model.Textures.Length) - return false; - - // Get the texture - var texture = item.Model.Textures[index]; - if (texture == null) - return false; - - // Read the data - var data = CreateTextureData(texture); - if (data == null) - return false; - - // Create the filename - string filename = $"{texture.Name}.bmp"; - - // If we have an invalid output directory - if (string.IsNullOrEmpty(outputDirectory)) - return false; - - // Create the full output path - filename = Path.Combine(outputDirectory, filename); - - // Ensure the output directory is created - var directoryName = Path.GetDirectoryName(filename); - if (directoryName != null) - Directory.CreateDirectory(directoryName); - - // Try to write the data - try - { - // Open the output file for writing - using (Stream fs = File.OpenWrite(filename)) - { - fs.Write(data, 0, data.Length); - } - } - catch - { - return false; - } - - return true; - } - - /// - /// Create a bitmap from the texture and palette data - /// - /// Texture object to format - /// Byte array representing the texture as a bitmap - private static byte[]? CreateTextureData(SabreTools.Models.BSP.Texture texture) - { - // If there's no palette data - if (texture.PaletteData == null || texture.PaletteData.Length == 0) - return null; - - // If there's no texture data - if (texture.TextureData == null || texture.TextureData.Length == 0) - return null; - - // Create the bitmap file header - var fileHeader = new SabreTools.Models.BMP.BITMAPFILEHEADER() - { - Type = ('M' << 8) | 'B', - Size = 14 + 40 + (texture.PaletteSize * 4) + (texture.Width * texture.Height), - OffBits = 14 + 40 + (texture.PaletteSize * 4), - }; - - // Create the bitmap info header - var infoHeader = new SabreTools.Models.BMP.BITMAPINFOHEADER - { - Size = 40, - Width = (int)texture.Width, - Height = (int)texture.Height, - Planes = 1, - BitCount = 8, - SizeImage = 0, - ClrUsed = texture.PaletteSize, - ClrImportant = texture.PaletteSize, - }; - - // Reformat the palette data - byte[] paletteData = new byte[texture.PaletteSize * 4]; - for (uint i = 0; i < texture.PaletteSize; i++) - { - paletteData[i * 4 + 0] = texture.PaletteData[i * 3 + 2]; - paletteData[i * 4 + 1] = texture.PaletteData[i * 3 + 1]; - paletteData[i * 4 + 2] = texture.PaletteData[i * 3 + 0]; - paletteData[i * 4 + 3] = 0; - } - - // Reformat the pixel data - byte[] pixelData = new byte[texture.Width * texture.Height]; - for (uint i = 0; i < texture.Width; i++) - { - for (uint j = 0; j < texture.Height; j++) - { - pixelData[i + ((texture.Height - 1 - j) * texture.Width)] = texture.TextureData[i + j * texture.Width]; - } - } - - // Build the file data - List buffer = new List(); - - // Bitmap file header - buffer.AddRange(BitConverter.GetBytes(fileHeader.Type)); - buffer.AddRange(BitConverter.GetBytes(fileHeader.Size)); - buffer.AddRange(BitConverter.GetBytes(fileHeader.Reserved1)); - buffer.AddRange(BitConverter.GetBytes(fileHeader.Reserved2)); - buffer.AddRange(BitConverter.GetBytes(fileHeader.OffBits)); - - // Bitmap info header - buffer.AddRange(BitConverter.GetBytes(infoHeader.Size)); - buffer.AddRange(BitConverter.GetBytes(infoHeader.Width)); - buffer.AddRange(BitConverter.GetBytes(infoHeader.Height)); - buffer.AddRange(BitConverter.GetBytes(infoHeader.Planes)); - buffer.AddRange(BitConverter.GetBytes(infoHeader.BitCount)); - buffer.AddRange(BitConverter.GetBytes(infoHeader.Compression)); - buffer.AddRange(BitConverter.GetBytes(infoHeader.SizeImage)); - buffer.AddRange(BitConverter.GetBytes(infoHeader.XPelsPerMeter)); - buffer.AddRange(BitConverter.GetBytes(infoHeader.YPelsPerMeter)); - buffer.AddRange(BitConverter.GetBytes(infoHeader.ClrUsed)); - buffer.AddRange(BitConverter.GetBytes(infoHeader.ClrImportant)); - - // Palette data - buffer.AddRange(paletteData); - - // Pixel data - buffer.AddRange(pixelData); - - return buffer.ToArray(); - } } } diff --git a/BinaryObjectScanner/FileType/GCF.cs b/BinaryObjectScanner/FileType/GCF.cs index 620017fd..1b39dbb3 100644 --- a/BinaryObjectScanner/FileType/GCF.cs +++ b/BinaryObjectScanner/FileType/GCF.cs @@ -126,19 +126,18 @@ namespace BinaryObjectScanner.FileType try { // Open the output file for writing - using (Stream fs = File.OpenWrite(filename)) - { - // Now read the data sequentially and write out while we have data left - long fileSize = file.Size; - for (int i = 0; i < dataBlockOffsets.Count; i++) - { - int readSize = (int)Math.Min(item.Model.DataBlockHeader?.BlockSize ?? 0, fileSize); - var data = item.ReadFromDataSource((int)dataBlockOffsets[i], readSize); - if (data == null) - return false; + using Stream fs = File.OpenWrite(filename); - fs.Write(data, 0, data.Length); - } + // Now read the data sequentially and write out while we have data left + long fileSize = file.Size; + for (int i = 0; i < dataBlockOffsets.Count; i++) + { + int readSize = (int)Math.Min(item.Model.DataBlockHeader?.BlockSize ?? 0, fileSize); + var data = item.ReadFromDataSource((int)dataBlockOffsets[i], readSize); + if (data == null) + return false; + + fs.Write(data, 0, data.Length); } } catch diff --git a/BinaryObjectScanner/FileType/PAK.cs b/BinaryObjectScanner/FileType/PAK.cs index 90979060..12d78d3b 100644 --- a/BinaryObjectScanner/FileType/PAK.cs +++ b/BinaryObjectScanner/FileType/PAK.cs @@ -108,10 +108,8 @@ namespace BinaryObjectScanner.FileType try { // Open the output file for writing - using (Stream fs = File.OpenWrite(filename)) - { - fs.Write(data, 0, data.Length); - } + using Stream fs = File.OpenWrite(filename); + fs.Write(data, 0, data.Length); } catch { diff --git a/BinaryObjectScanner/FileType/VBSP.cs b/BinaryObjectScanner/FileType/VBSP.cs index a154dd86..520dde12 100644 --- a/BinaryObjectScanner/FileType/VBSP.cs +++ b/BinaryObjectScanner/FileType/VBSP.cs @@ -1,6 +1,7 @@ using System; using System.IO; using BinaryObjectScanner.Interfaces; +using SabreTools.Models.BSP; namespace BinaryObjectScanner.FileType { @@ -29,6 +30,8 @@ namespace BinaryObjectScanner.FileType if (vbsp == null) return false; + // TODO: Introduce helper methods for all specialty lump types + // Loop through and extract all files Directory.CreateDirectory(outDir); ExtractAllLumps(vbsp, outDir); @@ -91,12 +94,12 @@ namespace BinaryObjectScanner.FileType // Create the filename string filename = $"lump_{index}.bin"; - switch (index) + switch ((LumpType)index) { - case SabreTools.Models.VBSP.Constants.HL_VBSP_LUMP_ENTITIES: + case LumpType.LUMP_ENTITIES: filename = "entities.ent"; break; - case SabreTools.Models.VBSP.Constants.HL_VBSP_LUMP_PAKFILE: + case LumpType.LUMP_PAKFILE: filename = "pakfile.zip"; break; } @@ -117,10 +120,8 @@ namespace BinaryObjectScanner.FileType try { // Open the output file for writing - using (Stream fs = File.OpenWrite(filename)) - { - fs.Write(data, 0, data.Length); - } + using Stream fs = File.OpenWrite(filename); + fs.Write(data, 0, data.Length); } catch { diff --git a/BinaryObjectScanner/FileType/VPK.cs b/BinaryObjectScanner/FileType/VPK.cs index abf5b261..2ab2ab43 100644 --- a/BinaryObjectScanner/FileType/VPK.cs +++ b/BinaryObjectScanner/FileType/VPK.cs @@ -165,10 +165,8 @@ namespace BinaryObjectScanner.FileType try { // Open the output file for writing - using (Stream fs = File.OpenWrite(filename)) - { - fs.Write(data, 0, data.Length); - } + using Stream fs = File.OpenWrite(filename); + fs.Write(data, 0, data.Length); } catch { diff --git a/BinaryObjectScanner/FileType/WAD.cs b/BinaryObjectScanner/FileType/WAD3.cs similarity index 80% rename from BinaryObjectScanner/FileType/WAD.cs rename to BinaryObjectScanner/FileType/WAD3.cs index d6fcb4e4..916f7f3f 100644 --- a/BinaryObjectScanner/FileType/WAD.cs +++ b/BinaryObjectScanner/FileType/WAD3.cs @@ -7,7 +7,7 @@ namespace BinaryObjectScanner.FileType /// /// Half-Life Texture Package File /// - public class WAD : IExtractable + public class WAD3 : IExtractable { /// public bool Extract(string file, string outDir, bool includeDebug) @@ -25,7 +25,7 @@ namespace BinaryObjectScanner.FileType try { // Create the wrapper - var wad = SabreTools.Serialization.Wrappers.WAD.Create(stream); + var wad = SabreTools.Serialization.Wrappers.WAD3.Create(stream); if (wad == null) return false; @@ -43,19 +43,19 @@ namespace BinaryObjectScanner.FileType } /// - /// Extract all lumps from the WAD to an output directory + /// Extract all lumps from the WAD3 to an output directory /// /// Output directory to write to /// True if all lumps extracted, false otherwise - public static bool ExtractAllLumps(SabreTools.Serialization.Wrappers.WAD item, string outputDirectory) + public static bool ExtractAllLumps(SabreTools.Serialization.Wrappers.WAD3 item, string outputDirectory) { // If we have no lumps - if (item.Model.Lumps == null || item.Model.Lumps.Length == 0) + if (item.Model.DirEntries == null || item.Model.DirEntries.Length == 0) return false; // Loop through and extract all lumps to the output bool allExtracted = true; - for (int i = 0; i < item.Model.Lumps.Length; i++) + for (int i = 0; i < item.Model.DirEntries.Length; i++) { allExtracted &= ExtractLump(item, i, outputDirectory); } @@ -64,23 +64,23 @@ namespace BinaryObjectScanner.FileType } /// - /// Extract a lump from the WAD to an output directory by index + /// Extract a lump from the WAD3 to an output directory by index /// /// Lump index to extract /// Output directory to write to /// True if the lump extracted, false otherwise - public static bool ExtractLump(SabreTools.Serialization.Wrappers.WAD item, int index, string outputDirectory) + public static bool ExtractLump(SabreTools.Serialization.Wrappers.WAD3 item, int index, string outputDirectory) { // If we have no lumps - if (item.Model.Lumps == null || item.Model.Lumps.Length == 0) + if (item.Model.DirEntries == null || item.Model.DirEntries.Length == 0) return false; // If the lumps index is invalid - if (index < 0 || index >= item.Model.Lumps.Length) + if (index < 0 || index >= item.Model.DirEntries.Length) return false; // Get the lump - var lump = item.Model.Lumps[index]; + var lump = item.Model.DirEntries[index]; if (lump == null) return false; @@ -108,10 +108,8 @@ namespace BinaryObjectScanner.FileType try { // Open the output file for writing - using (Stream fs = File.OpenWrite(filename)) - { - fs.Write(data, 0, data.Length); - } + using Stream fs = File.OpenWrite(filename); + fs.Write(data, 0, data.Length); } catch { diff --git a/BinaryObjectScanner/FileType/XZP.cs b/BinaryObjectScanner/FileType/XZP.cs index 7c7205d4..d1a174a2 100644 --- a/BinaryObjectScanner/FileType/XZP.cs +++ b/BinaryObjectScanner/FileType/XZP.cs @@ -117,10 +117,8 @@ namespace BinaryObjectScanner.FileType try { // Open the output file for writing - using (Stream fs = File.OpenWrite(filename)) - { - fs.Write(data, 0, data.Length); - } + using Stream fs = File.OpenWrite(filename); + fs.Write(data, 0, data.Length); } catch { diff --git a/ExtractionTool/ExtractionTool.csproj b/ExtractionTool/ExtractionTool.csproj index f36462fc..079b39da 100644 --- a/ExtractionTool/ExtractionTool.csproj +++ b/ExtractionTool/ExtractionTool.csproj @@ -36,7 +36,7 @@ - + \ No newline at end of file diff --git a/ExtractionTool/Program.cs b/ExtractionTool/Program.cs index 15a52909..4770cb55 100644 --- a/ExtractionTool/Program.cs +++ b/ExtractionTool/Program.cs @@ -388,15 +388,15 @@ namespace ExtractionTool vpk.Extract(stream, file, outputDirectory, includeDebug: true); } - // WAD + // WAD3 else if (ft == WrapperType.WAD) { // Build the archive information - Console.WriteLine("Extracting WAD contents"); + Console.WriteLine("Extracting WAD3 contents"); Console.WriteLine(); // Extract using the FileType - var wad = new WAD(); + var wad = new WAD3(); wad.Extract(stream, file, outputDirectory, includeDebug: true); } diff --git a/README.md b/README.md index c4467ce6..9b80fe6b 100644 --- a/README.md +++ b/README.md @@ -209,7 +209,7 @@ Below is a list of container formats that are supported in some way: | Half-Life Game Cache File (GCF) | Yes | Yes | Yes | | | Half-Life Level (BSP) | Yes | Yes | Yes | | | Half-Life Package File (PAK) | Yes | Yes | Yes | | -| Half-Life Texture Package File (WAD) | Yes | Yes | Yes | | +| Half-Life Texture Package File (WAD3) | Yes | Yes | Yes | | | Half-Life 2 Level (VBSP) | Yes | Yes | Yes | | | INI configuration file | No | No | No | Used in other detections currently | | InstallShield Archive V3 (Z) | No | Yes | Yes | Via `UnshieldSharp` |