Editor/Decompiler/StructReader.cs
namespace BspImport.Decompiler;

public class StructReader<T> where T : struct
{
	public byte[] byteBuf;

	public StructReader()
	{
		byteBuf = new byte[Marshal.SizeOf<T>()];
	}

	public IEnumerable<T> ReadMultiple( IEnumerable<byte> bytes, int num = 0 )
	{
		for ( int i = 0; i < num; i++ )
		{
			yield return Read( bytes );
			bytes = bytes.Skip( Marshal.SizeOf<T>() );
		}
	}

	public IEnumerable<T> ReadMultiple( IEnumerable<byte> bytes )
	{
		if ( bytes.Count() == 0 ) throw new ArgumentException( "No Data" );
		// input array length is multiple of base array length, to make sure it's a collection of our data type
		if ( bytes.Count() % byteBuf.Length != 0 )
		{
			throw new ArgumentException( $"Bad Data. Expected multiple of {byteBuf.Length}, got {bytes.Count()}." );
		}

		while ( bytes.Count() >= byteBuf.Length )
		{
			Array.Copy( bytes.ToArray(), byteBuf, Marshal.SizeOf<T>() );
			bytes = bytes.Skip( Marshal.SizeOf<T>() ).ToArray();

			object? result;
			GCHandle handle = GCHandle.Alloc( byteBuf, GCHandleType.Pinned );
			try
			{
				result = Marshal.PtrToStructure( handle.AddrOfPinnedObject(), typeof( T ) );
			}
			finally
			{
				handle.Free();
			}

			if ( result is null )
			{
				result = default( T );
			}

			yield return (T)result;
		}
	}

	public T Read( IEnumerable<byte> bytes )
	{
		Array.Copy( bytes.ToArray(), byteBuf, Marshal.SizeOf<T>() );
		if ( bytes.Count() == 0 ) throw new InvalidOperationException( "<EOF>" );

		object? result;
		GCHandle handle = GCHandle.Alloc( byteBuf, GCHandleType.Pinned );
		try
		{
			result = Marshal.PtrToStructure( handle.AddrOfPinnedObject(), typeof( T ) );
		}
		finally
		{
			handle.Free();
		}

		if ( result is null )
		{
			result = default( T );
		}

		return (T)result;
	}
}