Editor/Decompiler/Lumps/StaticPropLump.cs
using BspImport.Decompiler.Formats;

namespace BspImport.Decompiler.Lumps;

public class StaticPropLump : BaseLump
{
	private const int NameEntrySize = 128;

	private int DictEntryCount { get; set; }
	private Dictionary<int, string> Names { get; set; } = new();

	public StaticPropLump( ImportContext context, byte[] data, int version = 0 ) : base( context, data, version ) { }

	protected override void Parse( BinaryReader reader )
	{
		if ( reader.GetLength() < sizeof( int ) )
			return;

		DictEntryCount = reader.ReadInt32();
		if ( DictEntryCount < 0 )
			return;

		Names = new Dictionary<int, string>( DictEntryCount );

		for ( int i = 0; i < DictEntryCount; i++ )
		{
			if ( reader.GetLength() < NameEntrySize )
			{
				Log.Warning( $"[BSP] Static prop dictionary truncated at entry {i}/{DictEntryCount}." );
				return;
			}

			string entry = Encoding.ASCII.GetString( reader.ReadBytes( NameEntrySize ) ).TrimEnd( '\0' );
			Names[i] = entry;
		}

		if ( reader.GetLength() < sizeof( int ) )
			return;

		int leafRefCount = reader.ReadInt32();
		if ( leafRefCount < 0 )
			return;

		long leafRefBytes = (long)leafRefCount * sizeof( ushort );
		if ( reader.GetLength() < leafRefBytes )
		{
			Log.Warning( $"[BSP] Static prop leaf refs truncated: expected {leafRefCount} entries." );
			return;
		}

		reader.Skip<ushort>( leafRefCount );

		if ( reader.GetLength() < sizeof( int ) )
			return;

		int entryCount = reader.ReadInt32();
		if ( entryCount <= 0 )
			return;

		var structReaders = Context.FormatDescriptor.GetStructReaders( Context.BspVersion );
		var props = new List<LumpEntity>( entryCount );
		var propLength = reader.GetLength() / entryCount;

		for ( int i = 0; i < entryCount; i++ )
		{

			StaticPropInstance staticProp;
			try
			{
				var propReader = reader.Split( propLength );
				staticProp = structReaders.ReadStaticProp( propReader, Context.FormatDescriptor, Version );
			}
			catch ( Exception ex ) when ( ex is ArgumentException or EndOfStreamException or InvalidOperationException )
			{
				Log.Warning( $"[BSP] Static prop entries truncated at entry {i}/{entryCount}: {ex.Message}" );
				break;
			}

			var prop = new LumpEntity();
			prop.SetClassName( "prop_static" );
			prop.SetPosition( staticProp.Origin );
			prop.SetAngles( new Angles( staticProp.Angles ) );

			if ( Names.TryGetValue( staticProp.PropType, out var model ) )
			{
				prop.SetModel( model );
			}

			props.Add( prop );
		}

		if ( props.Count == 0 )
			return;

		Context.Entities = Context.Entities is { Length: > 0 }
			? Context.Entities.Concat( props ).ToArray()
			: props.ToArray();

		//Log.Info( $"STATIC PROPS: {entryCount}" );
	}
}