Some texfile formatting.

This commit is contained in:
Ottermandias 2023-08-01 13:11:57 +02:00
parent a95877b9e4
commit 2da6a33a62

View file

@ -8,36 +8,30 @@ namespace Penumbra.Import.Textures;
public static class TexFileParser public static class TexFileParser
{ {
public static ScratchImage Parse( Stream data ) public static ScratchImage Parse(Stream data)
{ {
using var r = new BinaryReader( data ); using var r = new BinaryReader(data);
var header = r.ReadStructure< TexFile.TexHeader >(); var header = r.ReadStructure<TexFile.TexHeader>();
var meta = header.ToTexMeta(); var meta = header.ToTexMeta();
if( meta.Format == DXGIFormat.Unknown ) if (meta.Format == DXGIFormat.Unknown)
{ throw new Exception($"Could not convert format {header.Format} to DXGI Format.");
throw new Exception( $"Could not convert format {header.Format} to DXGI Format." );
}
if( meta.Dimension == TexDimension.Unknown ) if (meta.Dimension == TexDimension.Unknown)
{ throw new Exception($"Could not obtain dimensionality from {header.Type}.");
throw new Exception( $"Could not obtain dimensionality from {header.Type}." );
}
meta.MipLevels = CountMipLevels( data, in meta, in header ); meta.MipLevels = CountMipLevels(data, in meta, in header);
if( meta.MipLevels == 0 ) if (meta.MipLevels == 0)
{ throw new Exception("Could not load file. Image is corrupted and does not contain enough data for its size.");
throw new Exception( "Could not load file. Image is corrupted and does not contain enough data for its size." );
}
var scratch = ScratchImage.Initialize( meta ); var scratch = ScratchImage.Initialize(meta);
CopyData( scratch, r ); CopyData(scratch, r);
return scratch; return scratch;
} }
private static unsafe int CountMipLevels( Stream data, in TexMeta meta, in TexFile.TexHeader header ) private static unsafe int CountMipLevels(Stream data, in TexMeta meta, in TexFile.TexHeader header)
{ {
var width = meta.Width; var width = meta.Width;
var height = meta.Height; var height = meta.Height;
@ -46,28 +40,22 @@ public static class TexFileParser
var lastOffset = 0L; var lastOffset = 0L;
var lastSize = 80L; var lastSize = 80L;
var minSize = meta.Format.IsCompressed() ? 4 : 1; var minSize = meta.Format.IsCompressed() ? 4 : 1;
for( var i = 0; i < 13; ++i ) for (var i = 0; i < 13; ++i)
{ {
var offset = header.OffsetToSurface[ i ]; var offset = header.OffsetToSurface[i];
if( offset == 0 ) if (offset == 0)
{
return i; return i;
}
var requiredSize = width * height * bits / 8; var requiredSize = width * height * bits / 8;
if( offset + requiredSize > data.Length ) if (offset + requiredSize > data.Length)
{
return i; return i;
}
var diff = offset - lastOffset; var diff = offset - lastOffset;
if( diff != lastSize ) if (diff != lastSize)
{
return i; return i;
}
width = Math.Max( width / 2, minSize ); width = Math.Max(width / 2, minSize);
height = Math.Max( height / 2, minSize ); height = Math.Max(height / 2, minSize);
lastOffset = offset; lastOffset = offset;
lastSize = requiredSize; lastSize = requiredSize;
} }
@ -75,48 +63,45 @@ public static class TexFileParser
return 13; return 13;
} }
private static unsafe void CopyData( ScratchImage image, BinaryReader r ) private static unsafe void CopyData(ScratchImage image, BinaryReader r)
{ {
fixed( byte* ptr = image.Pixels ) fixed (byte* ptr = image.Pixels)
{ {
var span = new Span< byte >( ptr, image.Pixels.Length ); var span = new Span<byte>(ptr, image.Pixels.Length);
var readBytes = r.Read( span ); var readBytes = r.Read(span);
if( readBytes < image.Pixels.Length ) if (readBytes < image.Pixels.Length)
{ throw new Exception($"Invalid data length {readBytes} < {image.Pixels.Length}.");
throw new Exception( $"Invalid data length {readBytes} < {image.Pixels.Length}." );
}
} }
} }
public static void Write( this TexFile.TexHeader header, BinaryWriter w ) public static void Write(this TexFile.TexHeader header, BinaryWriter w)
{ {
w.Write( ( uint )header.Type ); w.Write((uint)header.Type);
w.Write( ( uint )header.Format ); w.Write((uint)header.Format);
w.Write( header.Width ); w.Write(header.Width);
w.Write( header.Height ); w.Write(header.Height);
w.Write( header.Depth ); w.Write(header.Depth);
w.Write( header.MipLevels ); w.Write((byte) header.MipLevels);
w.Write((byte) 0); // TODO Lumina Update
unsafe unsafe
{ {
w.Write( header.LodOffset[ 0 ] ); w.Write(header.LodOffset[0]);
w.Write( header.LodOffset[ 1 ] ); w.Write(header.LodOffset[1]);
w.Write( header.LodOffset[ 2 ] ); w.Write(header.LodOffset[2]);
for( var i = 0; i < 13; ++i ) for (var i = 0; i < 13; ++i)
{ w.Write(header.OffsetToSurface[i]);
w.Write( header.OffsetToSurface[ i ] );
}
} }
} }
public static TexFile.TexHeader ToTexHeader( this ScratchImage scratch ) public static TexFile.TexHeader ToTexHeader(this ScratchImage scratch)
{ {
var meta = scratch.Meta; var meta = scratch.Meta;
var ret = new TexFile.TexHeader() var ret = new TexFile.TexHeader()
{ {
Height = ( ushort )meta.Height, Height = (ushort)meta.Height,
Width = ( ushort )meta.Width, Width = (ushort)meta.Width,
Depth = ( ushort )Math.Max( meta.Depth, 1 ), Depth = (ushort)Math.Max(meta.Depth, 1),
MipLevels = ( ushort )Math.Min( meta.MipLevels, 12 ), MipLevels = (byte)Math.Min(meta.MipLevels, 12),
Format = meta.Format.ToTexFormat(), Format = meta.Format.ToTexFormat(),
Type = meta.Dimension switch Type = meta.Dimension switch
{ {
@ -128,50 +113,48 @@ public static class TexFileParser
}, },
}; };
ret.FillSurfaceOffsets( scratch ); ret.FillSurfaceOffsets(scratch);
return ret; return ret;
} }
private static unsafe void FillSurfaceOffsets( this ref TexFile.TexHeader header, ScratchImage scratch ) private static unsafe void FillSurfaceOffsets(this ref TexFile.TexHeader header, ScratchImage scratch)
{ {
var idx = 0; var idx = 0;
fixed( byte* ptr = scratch.Pixels ) fixed (byte* ptr = scratch.Pixels)
{ {
foreach( var image in scratch.Images ) foreach (var image in scratch.Images)
{ {
var offset = ( byte* )image.Pixels - ptr; var offset = (byte*)image.Pixels - ptr;
header.OffsetToSurface[ idx++ ] = ( uint )( 80 + offset ); header.OffsetToSurface[idx++] = (uint)(80 + offset);
} }
} }
for( ; idx < 13; ++idx ) for (; idx < 13; ++idx)
{ header.OffsetToSurface[idx] = 0;
header.OffsetToSurface[ idx ] = 0;
}
header.LodOffset[ 0 ] = 0; header.LodOffset[0] = 0;
header.LodOffset[ 1 ] = 1; header.LodOffset[1] = 1;
header.LodOffset[ 2 ] = 2; header.LodOffset[2] = 2;
} }
public static TexMeta ToTexMeta( this TexFile.TexHeader header ) public static TexMeta ToTexMeta(this TexFile.TexHeader header)
=> new() => new()
{ {
Height = header.Height, Height = header.Height,
Width = header.Width, Width = header.Width,
Depth = Math.Max( header.Depth, ( ushort )1 ), Depth = Math.Max(header.Depth, (ushort)1),
MipLevels = header.MipLevels, MipLevels = header.MipLevels,
ArraySize = 1, ArraySize = 1,
Format = header.Format.ToDXGI(), Format = header.Format.ToDXGI(),
Dimension = header.Type.ToDimension(), Dimension = header.Type.ToDimension(),
MiscFlags = header.Type.HasFlag( TexFile.Attribute.TextureTypeCube ) ? D3DResourceMiscFlags.TextureCube : 0, MiscFlags = header.Type.HasFlag(TexFile.Attribute.TextureTypeCube) ? D3DResourceMiscFlags.TextureCube : 0,
MiscFlags2 = 0, MiscFlags2 = 0,
}; };
private static TexDimension ToDimension( this TexFile.Attribute attribute ) private static TexDimension ToDimension(this TexFile.Attribute attribute)
=> ( attribute & TexFile.Attribute.TextureTypeMask ) switch => (attribute & TexFile.Attribute.TextureTypeMask) switch
{ {
TexFile.Attribute.TextureType1D => TexDimension.Tex1D, TexFile.Attribute.TextureType1D => TexDimension.Tex1D,
TexFile.Attribute.TextureType2D => TexDimension.Tex2D, TexFile.Attribute.TextureType2D => TexDimension.Tex2D,
@ -179,7 +162,7 @@ public static class TexFileParser
_ => TexDimension.Unknown, _ => TexDimension.Unknown,
}; };
public static TexFile.TextureFormat ToTexFormat( this DXGIFormat format ) public static TexFile.TextureFormat ToTexFormat(this DXGIFormat format)
=> format switch => format switch
{ {
DXGIFormat.R8UNorm => TexFile.TextureFormat.L8, DXGIFormat.R8UNorm => TexFile.TextureFormat.L8,
@ -204,7 +187,7 @@ public static class TexFileParser
_ => TexFile.TextureFormat.Unknown, _ => TexFile.TextureFormat.Unknown,
}; };
public static DXGIFormat ToDXGI( this TexFile.TextureFormat format ) public static DXGIFormat ToDXGI(this TexFile.TextureFormat format)
=> format switch => format switch
{ {
TexFile.TextureFormat.L8 => DXGIFormat.R8UNorm, TexFile.TextureFormat.L8 => DXGIFormat.R8UNorm,
@ -229,4 +212,4 @@ public static class TexFileParser
TexFile.TextureFormat.Shadow24 => DXGIFormat.R24G8Typeless, TexFile.TextureFormat.Shadow24 => DXGIFormat.R24G8Typeless,
_ => DXGIFormat.Unknown, _ => DXGIFormat.Unknown,
}; };
} }