public override void FromFragment(ErpFragment fragment) { using (var memData = fragment.GetDataStream(true)) using (ErpBinaryReader reader = new ErpBinaryReader(memData)) { Unknown = reader.ReadInt32(); Unknown2 = reader.ReadInt32(); ImageType = (ErpGfxSurfaceFormat)reader.ReadInt32(); Width = reader.ReadUInt32(); Height = reader.ReadUInt32(); Unknown3 = reader.ReadInt32(); MipMapCount = reader.ReadUInt32(); ArraySize = reader.ReadUInt32(); Unknown4 = reader.ReadInt32(); _leftoverBytes = reader.BaseStream.Length - reader.BaseStream.Position; if (_leftoverBytes == 4) { // noticed this extra field in F1 2020, not sure if in earlier ones Unknown5 = reader.ReadInt32(); } else if (_leftoverBytes > 0) { throw new NotSupportedException("The GfxSurfaceRes0 data is not supported."); } } }
public override void FromFragment(ErpFragment fragment) { using (var memData = fragment.GetDataStream(true)) using (ErpBinaryReader reader = new ErpBinaryReader(memData)) { MipMapFileName = reader.ReadString(reader.ReadByte()); UInt32 mipMapCount = reader.ReadUInt32(); Mips = new List <ErpGfxSurfaceRes2Mips>((int)mipMapCount); for (int i = 0; i < mipMapCount; ++i) { ErpGfxSurfaceRes2Mips mip = new ErpGfxSurfaceRes2Mips(); mip.Compression = (ErpCompressionAlgorithm)reader.ReadByte(); mip.Offset = reader.ReadUInt64(); mip.PackedSize = reader.ReadUInt64(); mip.Size = reader.ReadUInt64(); Mips.Add(mip); } long leftoverBytes = reader.BaseStream.Length - reader.BaseStream.Position; if (leftoverBytes == 8) { // This part was introduced in F1 2017 hasTwoUnknowns = true; Unknown = reader.ReadSingle(); Unknown2 = reader.ReadSingle(); } else if (leftoverBytes > 0) { throw new NotSupportedException("The GfxSurfaceRes2 data is not supported."); } } }
public override void FromFragment(ErpFragment fragment) { using (var memData = fragment.GetDataStream(true)) using (ErpBinaryReader reader = new ErpBinaryReader(memData)) { MipMapFileName = reader.ReadString(reader.ReadByte()); UInt32 mipMapCount = reader.ReadUInt32(); Mips = new List <ErpGfxSurfaceRes2Mips>((int)mipMapCount); for (int i = 0; i < mipMapCount; ++i) { ErpGfxSurfaceRes2Mips mip = new ErpGfxSurfaceRes2Mips(); mip.Compression = (ErpCompressionAlgorithm)reader.ReadByte(); mip.Offset = reader.ReadUInt64(); mip.PackedSize = reader.ReadUInt64(); mip.Size = reader.ReadUInt64(); Mips.Add(mip); } // This part was introduces in F1 2017 if (reader.BaseStream.Length - reader.BaseStream.Position == 8) { hasTwoUnknowns = true; Unknown = reader.ReadSingle(); Unknown2 = reader.ReadSingle(); } } }
public override void FromFragment(ErpFragment fragment) { using var memData = fragment.GetDataStream(true); using var reader = new ErpBinaryReader(memData); Unknown = reader.ReadInt32(); ImageType = (ErpGfxSurfaceFormat)reader.ReadInt32(); Unknown2 = reader.ReadInt32(); MipMapCount = reader.ReadUInt32(); Unknown3 = reader.ReadInt32(); Unknown4 = reader.ReadInt32(); SurfaceResourceName = reader.ReadString(); }
public override void FromFragment(ErpFragment fragment) { using (var memData = fragment.GetDataStream(true)) using (ErpBinaryReader reader = new ErpBinaryReader(memData)) { Unknown = reader.ReadInt32(); Unknown2 = reader.ReadInt32(); ImageType = (ErpGfxSurfaceFormat)reader.ReadInt32(); Width = reader.ReadUInt32(); Height = reader.ReadUInt32(); Unknown3 = reader.ReadInt32(); MipMapCount = reader.ReadUInt32(); ArraySize = reader.ReadUInt32(); Unknown4 = reader.ReadInt32(); } }
public override void FromFragment(ErpFragment fragment) { using (var memData = fragment.GetDataStream(true)) using (ErpBinaryReader reader = new ErpBinaryReader(memData)) { MipMapFileName = reader.ReadString(reader.ReadByte()); UInt32 mipMapCount = reader.ReadUInt32(); Mips = new List <ErpGfxSurfaceRes2Mips>((int)mipMapCount); for (int i = 0; i < mipMapCount; ++i) { ErpGfxSurfaceRes2Mips mip = new ErpGfxSurfaceRes2Mips(); mip.Unknown = reader.ReadByte(); mip.Offset = reader.ReadUInt64(); mip.Width = reader.ReadUInt64(); mip.Height = reader.ReadUInt64(); Mips.Add(mip); } } }
public abstract void Read(ErpBinaryReader reader);
public override void Read(ErpBinaryReader reader) { throw new NotImplementedException(); }
public DdsFile ExportDDS(string fileName, bool isPreview, bool exportTexArray) { DdsFile dds = new DdsFile(); ErpGfxSRVResource srvRes = new ErpGfxSRVResource(); srvRes.FromResource(Texture); uint mipPower = (uint)Math.Pow(2.0, srvRes.SurfaceRes.Frag2.Mips.Count); uint mipWidth = 0, mipHeight = 0; ulong mipLinearSize = 0; ulong mipTotalSize = 0; if (srvRes.SurfaceRes.HasMips) { mipLinearSize = Math.Max(srvRes.SurfaceRes.Frag2.Mips[0].Width, srvRes.SurfaceRes.Frag2.Mips[0].Height); for (int i = 0; i < srvRes.SurfaceRes.Frag2.Mips.Count; ++i) { mipTotalSize += Math.Max(srvRes.SurfaceRes.Frag2.Mips[i].Width, srvRes.SurfaceRes.Frag2.Mips[i].Height); } mipWidth = srvRes.SurfaceRes.Fragment0.Width * mipPower; mipHeight = srvRes.SurfaceRes.Fragment0.Height * mipPower; } dds.header.width = srvRes.SurfaceRes.Fragment0.Width; dds.header.height = srvRes.SurfaceRes.Fragment0.Height; dds.header10.arraySize = srvRes.SurfaceRes.Fragment0.ArraySize; _texArraySize = srvRes.SurfaceRes.Fragment0.ArraySize; _textureInfo = srvRes.SurfaceRes.Fragment0.Width + "x" + srvRes.SurfaceRes.Fragment0.Height + " Mips:" + (srvRes.SurfaceRes.Fragment0.MipMapCount) + " Format:" + srvRes.SurfaceRes.Fragment0.ImageType + ","; switch (srvRes.SurfaceRes.Fragment0.ImageType) { //case (ErpGfxSurfaceFormat)14: // gameparticles k_smoke; application case ErpGfxSurfaceFormat.ABGR8: dds.header.flags |= DdsHeader.Flags.DDSD_LINEARSIZE; dds.header.pitchOrLinearSize = (srvRes.SurfaceRes.Fragment0.Width * srvRes.SurfaceRes.Fragment0.Height) * 4; if (srvRes.SurfaceRes.Fragment0.ArraySize > 1 && exportTexArray) { dds.header.ddspf.flags |= DdsPixelFormat.Flags.DDPF_FOURCC; dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0); dds.header10.dxgiFormat = DXGI_Format.DXGI_FORMAT_R8G8B8A8_UNORM; TextureInfo += "RGBA8"; } else { dds.header.ddspf.flags |= DdsPixelFormat.Flags.DDPF_ALPHAPIXELS | DdsPixelFormat.Flags.DDPF_RGB; dds.header.ddspf.fourCC = 0; dds.header.ddspf.rGBBitCount = 32; dds.header.ddspf.rBitMask = 0xFF; dds.header.ddspf.gBitMask = 0xFF00; dds.header.ddspf.bBitMask = 0xFF0000; dds.header.ddspf.aBitMask = 0xFF000000; TextureInfo += "ABGR8"; } break; case ErpGfxSurfaceFormat.DXT1: // ferrari_wheel_sfc dds.header.flags |= DdsHeader.Flags.DDSD_LINEARSIZE; dds.header.pitchOrLinearSize = (srvRes.SurfaceRes.Fragment0.Width * srvRes.SurfaceRes.Fragment0.Height) / 2; dds.header.ddspf.flags |= DdsPixelFormat.Flags.DDPF_FOURCC; if (srvRes.SurfaceRes.Fragment0.ArraySize > 1 && exportTexArray) { dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0); dds.header10.dxgiFormat = DXGI_Format.DXGI_FORMAT_BC1_UNORM; } else { dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DXT1"), 0); } TextureInfo += "DXT1"; break; case ErpGfxSurfaceFormat.DXT1_SRGB: // ferrari_wheel_df, ferrari_paint dds.header.flags |= DdsHeader.Flags.DDSD_LINEARSIZE; dds.header.pitchOrLinearSize = (srvRes.SurfaceRes.Fragment0.Width * srvRes.SurfaceRes.Fragment0.Height) / 2; dds.header.ddspf.flags |= DdsPixelFormat.Flags.DDPF_FOURCC; dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0); dds.header10.dxgiFormat = DXGI_Format.DXGI_FORMAT_BC1_UNORM_SRGB; TextureInfo += "BC1_SRGB"; break; case ErpGfxSurfaceFormat.DXT5: // ferrari_sfc dds.header.flags |= DdsHeader.Flags.DDSD_LINEARSIZE; dds.header.pitchOrLinearSize = (srvRes.SurfaceRes.Fragment0.Width * srvRes.SurfaceRes.Fragment0.Height); dds.header.ddspf.flags |= DdsPixelFormat.Flags.DDPF_FOURCC; if (srvRes.SurfaceRes.Fragment0.ArraySize > 1 && exportTexArray) { dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0); dds.header10.dxgiFormat = DXGI_Format.DXGI_FORMAT_BC3_UNORM; } else { dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DXT5"), 0); } TextureInfo += "DXT5"; break; case ErpGfxSurfaceFormat.DXT5_SRGB: // ferrari_decal dds.header.flags |= DdsHeader.Flags.DDSD_LINEARSIZE; dds.header.pitchOrLinearSize = (srvRes.SurfaceRes.Fragment0.Width * srvRes.SurfaceRes.Fragment0.Height); dds.header.ddspf.flags |= DdsPixelFormat.Flags.DDPF_FOURCC; dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0); dds.header10.dxgiFormat = DXGI_Format.DXGI_FORMAT_BC3_UNORM_SRGB; TextureInfo += "BC3_SRGB"; break; case ErpGfxSurfaceFormat.ATI1: // gameparticles k_smoke dds.header.flags |= DdsHeader.Flags.DDSD_LINEARSIZE; dds.header.pitchOrLinearSize = (srvRes.SurfaceRes.Fragment0.Width * srvRes.SurfaceRes.Fragment0.Height) / 2; dds.header.ddspf.flags |= DdsPixelFormat.Flags.DDPF_FOURCC; if (srvRes.SurfaceRes.Fragment0.ArraySize > 1 && exportTexArray) { dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0); dds.header10.dxgiFormat = DXGI_Format.DXGI_FORMAT_BC4_UNORM; } else { dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("ATI1"), 0); } TextureInfo += "ATI1"; break; case ErpGfxSurfaceFormat.ATI2: // ferrari_wheel_nm dds.header.flags |= DdsHeader.Flags.DDSD_LINEARSIZE; dds.header.pitchOrLinearSize = (srvRes.SurfaceRes.Fragment0.Width * srvRes.SurfaceRes.Fragment0.Height); dds.header.ddspf.flags |= DdsPixelFormat.Flags.DDPF_FOURCC; if (srvRes.SurfaceRes.Fragment0.ArraySize > 1 && exportTexArray) { dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0); dds.header10.dxgiFormat = DXGI_Format.DXGI_FORMAT_BC5_UNORM; } else { dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("ATI2"), 0); } TextureInfo += "ATI2/3Dc"; break; case ErpGfxSurfaceFormat.BC6: // key0_2016; environment abu_dhabi tree_palm_06 dds.header.flags |= DdsHeader.Flags.DDSD_LINEARSIZE; dds.header.pitchOrLinearSize = (srvRes.SurfaceRes.Fragment0.Width * srvRes.SurfaceRes.Fragment0.Height); dds.header.ddspf.flags |= DdsPixelFormat.Flags.DDPF_FOURCC; dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0); dds.header10.dxgiFormat = DXGI_Format.DXGI_FORMAT_BC6H_UF16; TextureInfo += "BC6H_UF16"; break; case ErpGfxSurfaceFormat.BC7: dds.header.flags |= DdsHeader.Flags.DDSD_LINEARSIZE; dds.header.pitchOrLinearSize = (srvRes.SurfaceRes.Fragment0.Width * srvRes.SurfaceRes.Fragment0.Height); dds.header.ddspf.flags |= DdsPixelFormat.Flags.DDPF_FOURCC; dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0); dds.header10.dxgiFormat = DXGI_Format.DXGI_FORMAT_BC7_UNORM; TextureInfo += "BC7"; break; case ErpGfxSurfaceFormat.BC7_SRGB: // flow_boot splash_bg_image dds.header.flags |= DdsHeader.Flags.DDSD_LINEARSIZE; dds.header.pitchOrLinearSize = (srvRes.SurfaceRes.Fragment0.Width * srvRes.SurfaceRes.Fragment0.Height); dds.header.ddspf.flags |= DdsPixelFormat.Flags.DDPF_FOURCC; dds.header.ddspf.fourCC = BitConverter.ToUInt32(Encoding.UTF8.GetBytes("DX10"), 0); dds.header10.dxgiFormat = DXGI_Format.DXGI_FORMAT_BC7_UNORM_SRGB; TextureInfo += "BC7_SRGB"; break; default: TextureInfo += "Unknown"; throw new Exception("Image format not supported!"); } if (srvRes.SurfaceRes.Fragment0.MipMapCount > 0) { dds.header.flags |= DdsHeader.Flags.DDSD_MIPMAPCOUNT; dds.header.mipMapCount = srvRes.SurfaceRes.Fragment0.MipMapCount; dds.header.caps |= DdsHeader.Caps.DDSCAPS_MIPMAP | DdsHeader.Caps.DDSCAPS_COMPLEX; } byte[] imageData = srvRes.SurfaceRes.Fragment1.Data; string mipMapFullFileName = Path.Combine(Properties.Settings.Default.F12016Dir, srvRes.SurfaceRes.Frag2.MipMapFileName); bool foundMipMapFile = File.Exists(mipMapFullFileName); bool hasValidMipMaps = dds.header.pitchOrLinearSize < mipLinearSize && dds.header.pitchOrLinearSize * mipPower * mipPower == mipLinearSize; if (srvRes.SurfaceRes.HasMips && hasValidMipMaps && (!isPreview || foundMipMapFile)) { if (!isPreview) { OpenFileDialog odialog = new OpenFileDialog(); odialog.Filter = "Mipmaps files|*.mipmaps|All files|*.*"; odialog.Title = "Select a mipmaps file"; odialog.FileName = Path.GetFileName(mipMapFullFileName); mipMapFullFileName = Path.GetDirectoryName(mipMapFullFileName); if (Directory.Exists(mipMapFullFileName)) { odialog.InitialDirectory = mipMapFullFileName; } foundMipMapFile = odialog.ShowDialog() == true; mipMapFullFileName = odialog.FileName; } if (foundMipMapFile) { byte[] mipImageData; using (ErpBinaryReader reader = new ErpBinaryReader(File.Open(mipMapFullFileName, FileMode.Open, FileAccess.Read, FileShare.Read))) { mipImageData = reader.ReadBytes((int)reader.BaseStream.Length); } dds.header.width = mipWidth; dds.header.height = mipHeight; dds.header.pitchOrLinearSize = (uint)mipLinearSize; if (mipTotalSize != (ulong)mipImageData.Length) { throw new Exception($"There is a mismatch with the mipmaps file.{Environment.NewLine}It is either incorrectly modded, or in the wrong folder."); } if (srvRes.SurfaceRes.Frag2.Mips.Count > 0) { dds.header.flags |= DdsHeader.Flags.DDSD_MIPMAPCOUNT; dds.header.mipMapCount += (uint)srvRes.SurfaceRes.Frag2.Mips.Count; dds.header.caps |= DdsHeader.Caps.DDSCAPS_MIPMAP | DdsHeader.Caps.DDSCAPS_COMPLEX; } dds.bdata = new byte[mipImageData.Length + imageData.Length]; Buffer.BlockCopy(mipImageData, 0, dds.bdata, 0, mipImageData.Length); Buffer.BlockCopy(imageData, 0, dds.bdata, mipImageData.Length, imageData.Length); dds.Write(File.Open(fileName, FileMode.Create, FileAccess.Write, FileShare.Read), -1); TextureInfo += Environment.NewLine + mipWidth + "x" + mipHeight + " Mips:" + (srvRes.SurfaceRes.Frag2.Mips.Count); } else { throw new FileNotFoundException("Mipmaps file not found!", mipMapFullFileName); } } else { if (srvRes.SurfaceRes.HasMips) { if (!hasValidMipMaps) { // This usually happens when the mips fragment was never updated with the new offsets/width/height/linearsize TextureInfo += Environment.NewLine + "Mipmaps are incorrectly modded! Try exporting, then importing to fix the issue."; } else if (!foundMipMapFile) { TextureInfo += Environment.NewLine + "Mipmaps not found! Make sure they are in the correct F1 game folder!"; } else { // Not found during preview TextureInfo += Environment.NewLine + "Mipmaps not loaded! Make sure they are in the correct F1 game folder!"; } } if (srvRes.SurfaceRes.Fragment0.ArraySize > 1) { uint bytesPerArrayImage = (uint)imageData.Length / srvRes.SurfaceRes.Fragment0.ArraySize; byte[] data = new byte[bytesPerArrayImage]; if (!exportTexArray) { dds.header10.arraySize = 1; Buffer.BlockCopy(imageData, (int)(bytesPerArrayImage * _texArrayIndex), data, 0, (int)bytesPerArrayImage); dds.bdata = data; dds.Write(File.Open(fileName, FileMode.Create, FileAccess.Write, FileShare.Read), -1); } else { dds.bdata = imageData; dds.Write(File.Open(fileName, FileMode.Create, FileAccess.Write, FileShare.Read), -1); // TODO: Add support for exporting individual tex array slices //string output = Path.Combine(Path.GetDirectoryName(fileName), Path.GetFileNameWithoutExtension(fileName)); //for (int i = 0; i < srvRes.SurfaceRes.Fragment0.ArraySize; ++i) //{ // Buffer.BlockCopy(imageData, (int)(bytesPerArrayImage * i), data, 0, (int)bytesPerArrayImage); // dds.bdata = data; // dds.Write(File.Open(output + "!!!" + i.ToString("000") + ".dds", FileMode.Create, FileAccess.Write, FileShare.Read), -1); //} } } else { dds.bdata = imageData; dds.Write(File.Open(fileName, FileMode.Create, FileAccess.Write, FileShare.Read), -1); } } return(dds); }