private static void WriteTextureOffsets(Stream stream, BspTexture[] textures) { stream.Write(textures.Length); var offset = 4 + textures.Length * 4; foreach (var texture in textures) { stream.Write(offset); if (texture.IsEmbedded) { offset += 40 + texture.ImageData.Sum(imageData => imageData.Length) + 2 + texture.Palette.Length * 3; offset += StreamExtensions.RequiredPadding(2 + texture.Palette.Length * 3, 4); } else { offset += 40; } } }
private static void WriteTexture(Stream stream, BspTexture texture) { stream.Write(texture.Name, 16); stream.Write(texture.Width); stream.Write(texture.Height); if (!texture.IsEmbedded) { for (int i = 0; i < 4; i++) { stream.Write(0u); } return; } else { var offset = 40u; foreach (var imageData in texture.ImageData) { stream.Write(offset); offset += (uint)imageData.Length; } foreach (var imageData in texture.ImageData) { if (imageData != null) { stream.Write(imageData); } } stream.Write((ushort)texture.Palette.Length); foreach (var color in texture.Palette) { stream.Write(color); } stream.Write(new byte[StreamExtensions.RequiredPadding(2 + texture.Palette.Length * 3, 4)]); } }
private static uint GetTextureFileSize(Texture texture) { if (texture.Type == TextureType.MipmapTexture) { var size = 40; size += texture.ImageData.Length; size += texture.Mipmap1Data.Length; size += texture.Mipmap2Data.Length; size += texture.Mipmap3Data.Length; size += 2; size += texture.Palette.Length * 3; size += StreamExtensions.RequiredPadding(2 + texture.Palette.Length * 3, 4); return((uint)size); } else if (texture.Type == TextureType.Font) { var size = 16; size += texture.CharInfos.Length * 4; size += texture.ImageData.Length; size += 2; size += texture.Palette.Length * 3; size += StreamExtensions.RequiredPadding(2 + texture.Palette.Length * 3, 4); return((uint)size); } else if (texture.Type == TextureType.SimpleTexture) { var size = 8; size += texture.ImageData.Length; size += 2; size += texture.Palette.Length * 3; size += StreamExtensions.RequiredPadding(2 + texture.Palette.Length * 3, 4); return((uint)size); } else { throw new NotSupportedException($"Texture type {texture.Type} is not supported."); } }
/// <summary> /// Removes embedded textures from the specified bsp file. /// If no output path is provided, then the input bsp file is overwritten. /// </summary> public static int RemoveEmbeddedTextures(string path, string outputPath = null) { // Read all bsp lump contents: var lumps = new List <Lump>(); var lumpsData = new List <byte[]>(); using (var inputStream = File.OpenRead(path)) { var bspHeader = ReadBspHeader(inputStream); lumps.AddRange(bspHeader.Lumps); lumpsData.AddRange(bspHeader.Lumps.Select(lump => ReadLumpData(inputStream, lump))); } // Remove image and palette data from embedded textures: var removedTextureCount = 0; BspTexture[] bspTextures; using (var texturesLumpstream = new MemoryStream(lumpsData[TexturesLumpIndex])) { var textureOffsets = ReadTextureOffsets(texturesLumpstream); bspTextures = new BspTexture[textureOffsets.Length]; for (int i = 0; i < textureOffsets.Length; i++) { texturesLumpstream.Seek(textureOffsets[i], SeekOrigin.Begin); var bspTexture = ReadTexture(texturesLumpstream); if (bspTexture.IsEmbedded) { bspTexture.ImageData = null; bspTexture.Palette = null; removedTextureCount += 1; } bspTextures[i] = bspTexture; } } // Create new texture lump data: using (var stream = new MemoryStream()) { WriteTextureOffsets(stream, bspTextures); foreach (var bspTexture in bspTextures) { WriteTexture(stream, bspTexture); } lumpsData[TexturesLumpIndex] = stream.ToArray(); } // Recalculate lump offsets/lengths: var lumpIndexes = lumps .Select((lump, i) => (lump, i)) .OrderBy(pair => pair.lump.Offset) .Select(pair => pair.i) .ToArray(); var offset = 4 + lumps.Count * 8; // 124, BSP header size (15 lumps) for (int i = 0; i < lumpIndexes.Length; i++) { var lumpIndex = lumpIndexes[i]; lumps[lumpIndex] = new Lump { Offset = offset, Length = lumpsData[lumpIndex].Length }; offset += lumpsData[lumpIndex].Length; offset += StreamExtensions.RequiredPadding(lumpsData[lumpIndex].Length, 4); } // Save the bsp file: using (var outputStream = File.OpenWrite(outputPath ?? path)) { WriteBspHeader(outputStream, new BspHeader { Version = 30, Lumps = lumps.ToArray() }); foreach (var lumpIndex in lumpIndexes) { WriteLumpData(outputStream, lumpsData[lumpIndex]); } } return(removedTextureCount); }
private static void WriteTexture(Stream stream, Texture texture) { if (texture.Type == TextureType.MipmapTexture) { stream.Write(texture.Name, 16); stream.Write((uint)texture.Width); stream.Write((uint)texture.Height); stream.Write((uint)40); stream.Write((uint)(40 + texture.ImageData.Length)); stream.Write((uint)(40 + texture.ImageData.Length + texture.Mipmap1Data.Length)); stream.Write((uint)(40 + texture.ImageData.Length + texture.Mipmap1Data.Length + texture.Mipmap2Data.Length)); stream.Write(texture.ImageData); stream.Write(texture.Mipmap1Data); stream.Write(texture.Mipmap2Data); stream.Write(texture.Mipmap3Data); stream.Write((ushort)texture.Palette.Length); if (texture.Type == TextureType.MipmapTexture) { foreach (var color in texture.Palette) { stream.Write(color); } } stream.Write(new byte[StreamExtensions.RequiredPadding(2 + texture.Palette.Length * 3, 4)]); } else if (texture.Type == TextureType.Font) { stream.Write((uint)texture.Width); stream.Write((uint)texture.Height); stream.Write((uint)texture.RowCount); stream.Write((uint)texture.RowHeight); foreach (var charInfo in texture.CharInfos) { stream.Write((ushort)charInfo.StartOffset); stream.Write((ushort)charInfo.CharWidth); } stream.Write(texture.ImageData); stream.Write((ushort)texture.Palette.Length); foreach (var color in texture.Palette) { stream.Write(color); } stream.Write(new byte[StreamExtensions.RequiredPadding(2 + texture.Palette.Length * 3, 4)]); } else if (texture.Type == TextureType.SimpleTexture) { stream.Write((uint)texture.Width); stream.Write((uint)texture.Height); stream.Write(texture.ImageData); stream.Write((ushort)texture.Palette.Length); foreach (var color in texture.Palette) { stream.Write(color); } stream.Write(new byte[StreamExtensions.RequiredPadding(2 + texture.Palette.Length * 3, 4)]); } else { throw new InvalidDataException($"Unknown texture type: {texture.Type}."); } }