Editor/Core/UnityFbxCompatibility.cs
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
namespace ImportUnityPackage;
/// <summary>Repairs a redundant array emitted by some legacy ASCII FBX exporters.</summary>
public static class UnityFbxCompatibility
{
public static byte[] Normalize( string source, CancellationToken cancel )
{
// Ordinary/binary models remain byte-for-byte copies. Bound optional text processing.
using var stream = File.OpenRead( source );
if ( stream.Length > 32 * 1024 * 1024 ) return null;
var header = new byte[32];
var length = stream.Read( header );
if ( !Encoding.ASCII.GetString( header, 0, length ).StartsWith( "; FBX ", StringComparison.Ordinal ) ) return null;
stream.Position = 0;
using var reader = new StreamReader( stream, new UTF8Encoding( false, true ), false );
string text;
try { text = reader.ReadToEnd(); }
catch ( DecoderFallbackException ) { return null; }
return NormalizeText( text, cancel ) is string normalized ? Encoding.UTF8.GetBytes( normalized ) : null;
}
internal static string NormalizeText( string text, CancellationToken cancel )
{
cancel.ThrowIfCancellationRequested();
const string numbers = @"[-+0-9.eE,\s]+";
var timeout = TimeSpan.FromSeconds( 2 );
var tails = Regex.Matches( text, @"(?m)^[ \t]*}(?<tail>,[-+0-9.eE, \t]+)(?=\r?$)", RegexOptions.None, timeout );
if ( tails.Count == 0 ) return null;
string Compact( string value ) => string.Concat( value.Where( c => !char.IsWhiteSpace( c ) ) ).Trim( ',' );
var vertices = Regex.Matches( text, @"(?m)^[ \t]*Vertices:[ \t]*(" + numbers + ")", RegexOptions.None, timeout )
.Select( m => Compact( m.Groups[1].Value ) ).Where( s => s.Length > 0 ).ToHashSet( StringComparer.Ordinal );
var output = new StringBuilder( text );
var changed = false;
foreach ( Match match in tails.Reverse() )
{
cancel.ThrowIfCancellationRequested();
var tail = match.Groups["tail"];
// Only discard an exact repeated vertex sequence at an invalid syntax position.
// Different data is left untouched so an uncertain repair cannot alter geometry.
if ( !vertices.Contains( Compact( tail.Value ) ) ) continue;
output.Remove( tail.Index, tail.Length );
changed = true;
}
return changed ? output.ToString() : null;
}
}