Add material file parsing and writing.

This commit is contained in:
Ottermandias 2022-08-13 21:20:32 +02:00
parent 5ac3a903f6
commit 5e9cb77415
2 changed files with 229 additions and 0 deletions

View file

@ -0,0 +1,133 @@
using System;
using System.IO;
using System.Text;
using Lumina.Data.Parsing;
using Lumina.Extensions;
namespace Penumbra.GameData.Files;
public partial class MtrlFile
{
public struct ColorSet
{
public string Name;
public ushort Index;
}
public struct Texture
{
public string Path;
public ushort Flags;
}
public struct Constant
{
public uint Id;
public uint Value;
}
public struct ShaderPackageData
{
public string Name;
public ShaderKey[] ShaderKeys;
public Constant[] Constants;
public Sampler[] Samplers;
public float[] ShaderValues;
public uint Unk;
}
public uint Version;
public Texture[] Textures;
public ColorSet[] UvColorSets;
public ColorSet[] ColorSets;
public ushort[] ColorSetData;
public ShaderPackageData ShaderPackage;
public byte[] AdditionalData;
public MtrlFile( byte[] data )
{
using var stream = new MemoryStream( data );
using var r = new BinaryReader( stream );
Version = r.ReadUInt32();
r.ReadUInt16(); // file size
var dataSetSize = r.ReadUInt16();
var stringTableSize = r.ReadUInt16();
var shaderPackageNameOffset = r.ReadUInt16();
var textureCount = r.ReadByte();
var uvSetCount = r.ReadByte();
var colorSetCount = r.ReadByte();
var additionalDataSize = r.ReadByte();
Textures = ReadTextureOffsets( r, textureCount, out var textureOffsets );
UvColorSets = ReadColorSetOffsets( r, uvSetCount, out var uvOffsets );
ColorSets = ReadColorSetOffsets( r, colorSetCount, out var colorOffsets );
var strings = r.ReadBytes( stringTableSize );
for( var i = 0; i < textureCount; ++i )
{
Textures[ i ].Path = UseOffset( strings, textureOffsets[ i ] );
}
for( var i = 0; i < uvSetCount; ++i )
{
UvColorSets[ i ].Name = UseOffset( strings, uvOffsets[ i ] );
}
for( var i = 0; i < colorSetCount; ++i )
{
ColorSets[ i ].Name = UseOffset( strings, colorOffsets[ i ] );
}
ShaderPackage.Name = UseOffset( strings, shaderPackageNameOffset );
AdditionalData = r.ReadBytes( additionalDataSize );
ColorSetData = r.ReadStructuresAsArray< ushort >( dataSetSize / 2 );
var shaderValueListSize = r.ReadUInt16();
var shaderKeyCount = r.ReadUInt16();
var constantCount = r.ReadUInt16();
var samplerCount = r.ReadUInt16();
ShaderPackage.Unk = r.ReadUInt32();
ShaderPackage.ShaderKeys = r.ReadStructuresAsArray< ShaderKey >( shaderKeyCount );
ShaderPackage.Constants = r.ReadStructuresAsArray< Constant >( constantCount );
ShaderPackage.Samplers = r.ReadStructuresAsArray< Sampler >( samplerCount );
ShaderPackage.ShaderValues = r.ReadStructuresAsArray< float >( shaderValueListSize / 4 );
}
private static Texture[] ReadTextureOffsets( BinaryReader r, int count, out ushort[] offsets )
{
var ret = new Texture[count];
offsets = new ushort[count];
for( var i = 0; i < count; ++i )
{
offsets[ i ] = r.ReadUInt16();
ret[ i ].Flags = r.ReadUInt16();
}
return ret;
}
private static ColorSet[] ReadColorSetOffsets( BinaryReader r, int count, out ushort[] offsets )
{
var ret = new ColorSet[count];
offsets = new ushort[count];
for( var i = 0; i < count; ++i )
{
offsets[ i ] = r.ReadUInt16();
ret[ i ].Index = r.ReadUInt16();
}
return ret;
}
private static string UseOffset( ReadOnlySpan< byte > strings, ushort offset )
{
strings = strings[ offset.. ];
var end = strings.IndexOf( ( byte )'\0' );
return Encoding.UTF8.GetString( strings[ ..end ] );
}
}