Editor/Conversion/Source1/BinaryUtil.cs

Utility class for reading binary data used by the importer. It reads null-terminated strings, reads a string at an offset relative to a base, reads 3D vector and quaternion values, and converts a quaternion to Euler angles in degrees using Source-style ordering (pitch, yaw, roll).

File Access
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace GmodAssetImporter;

static class BinaryUtil
{
	public static string ReadNullTerminated( BinaryReader br, int max )
	{
		var bytes = br.ReadBytes( max );
		var len = Array.IndexOf( bytes, (byte)0 );
		if ( len < 0 ) len = bytes.Length;
		return Encoding.ASCII.GetString( bytes, 0, len );
	}

	public static string ReadStringAt( BinaryReader br, long baseOffset, int relativeIndex )
	{
		if ( relativeIndex == 0 ) return "";
		var pos = br.BaseStream.Position;
		br.BaseStream.Position = baseOffset + relativeIndex;
		var chars = new List<byte>();
		while ( true )
		{
			var b = br.ReadByte();
			if ( b == 0 ) break;
			chars.Add( b );
			if ( chars.Count > 512 ) break;
		}
		br.BaseStream.Position = pos;
		return Encoding.ASCII.GetString( chars.ToArray() );
	}

	public static Vec3 ReadVec3( BinaryReader br ) => new( br.ReadSingle(), br.ReadSingle(), br.ReadSingle() );

	public static Quat ReadQuat( BinaryReader br ) => new( br.ReadSingle(), br.ReadSingle(), br.ReadSingle(), br.ReadSingle() );

	public static float[] QuaternionToEulerDegrees( Quat q )
	{
		// Source-style Euler (pitch, yaw, roll) approximate for SMD
		var sinrCosp = 2 * (q.W * q.X + q.Y * q.Z);
		var cosrCosp = 1 - 2 * (q.X * q.X + q.Y * q.Y);
		var roll = MathF.Atan2( sinrCosp, cosrCosp );

		var sinp = 2 * (q.W * q.Y - q.Z * q.X);
		var pitch = MathF.Abs( sinp ) >= 1 ? MathF.CopySign( MathF.PI / 2, sinp ) : MathF.Asin( sinp );

		var sinyCosp = 2 * (q.W * q.Z + q.X * q.Y);
		var cosyCosp = 1 - 2 * (q.Y * q.Y + q.Z * q.Z);
		var yaw = MathF.Atan2( sinyCosp, cosyCosp );

		const float r2d = 180f / MathF.PI;
		return new[] { pitch * r2d, yaw * r2d, roll * r2d };
	}
}