mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-04-18 13:29:22 -04:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
266 lines
7.2 KiB
C#
266 lines
7.2 KiB
C#
using Facepunch.InteropGen.Parsers;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
|
|
namespace Facepunch.InteropGen;
|
|
|
|
public class Definition
|
|
{
|
|
[ThreadStatic]
|
|
public static Definition Current;
|
|
|
|
/// <summary>
|
|
/// The root folder
|
|
/// </summary>
|
|
public System.IO.DirectoryInfo Root { get; private set; }
|
|
public string Ident { get; internal set; } = "!!!NO IDENT!!!";
|
|
public string NativeDll { get; internal set; } = "!!!NO NativeDll!!!";
|
|
public string InitFrom { get; internal set; } = "Native";
|
|
public string StringTools { get; internal set; } = "Sandbox.Interop";
|
|
public string Filename { get; private set; }
|
|
public string ExceptionHandlerName { get; internal set; }
|
|
public string SaveFileCpp { get; internal set; }
|
|
public string SaveFileCppH { get; internal set; }
|
|
public string SaveFileCs { get; internal set; }
|
|
public string ManagedNamespace { get; internal set; } = "ManagedNamespace";
|
|
public string PrecompiledHeader { get; set; }
|
|
public string FullText { get; set; }
|
|
public int Hash { get; private set; }
|
|
public string CustomNamespace => SaveFileCs.Replace( ".cs", "Internal" ).Replace( ".", "" ).Replace( "/", "" );
|
|
|
|
public List<Class> Classes = [];
|
|
public List<Struct> Structs = [];
|
|
public List<string> Includes = [];
|
|
public List<string> CppIncludes = [];
|
|
public List<string> Externs = [];
|
|
public List<string> FunctionPointers = [];
|
|
public List<string> Delegates = [];
|
|
public List<Definition> IncludedDefinitions = [];
|
|
public List<Definition> SkipDefines = [];
|
|
public List<Definition> SkipAll = [];
|
|
|
|
public static Definition FromFile( string filename )
|
|
{
|
|
string folder = System.IO.Path.GetDirectoryName( filename );
|
|
if ( string.IsNullOrEmpty( folder ) )
|
|
{
|
|
folder = ".";
|
|
}
|
|
|
|
// Log.WriteLine( $"Reading {filename}" );
|
|
|
|
string text = System.IO.File.ReadAllText( filename );
|
|
|
|
Definition d = new()
|
|
{
|
|
Filename = System.IO.Path.GetFileName( filename ),
|
|
Root = new System.IO.DirectoryInfo( folder )
|
|
};
|
|
|
|
Current = d;
|
|
|
|
// Log.WriteLine( $"Parsing {filename}" );
|
|
d.ParseFrom( text );
|
|
|
|
// Log.WriteLine( $"Completing definition" );
|
|
d.CompleteDefinition();
|
|
|
|
// Log.WriteLine( $"Sorting" );
|
|
d.Sort();
|
|
|
|
// Log.WriteLine( $"Mangling" );
|
|
Mangler mangler = new();
|
|
mangler.Mangle( d.Classes );
|
|
|
|
d.GenerateHash();
|
|
|
|
return d;
|
|
}
|
|
|
|
private void ParseFrom( string text )
|
|
{
|
|
FullText = "";
|
|
|
|
GlobalParser parser = new();
|
|
parser.Parse( this, text, System.IO.Path.Combine( Root.FullName, Filename ) );
|
|
}
|
|
|
|
private void GenerateHash()
|
|
{
|
|
using MD5 md5 = MD5.Create();
|
|
md5.Initialize();
|
|
_ = md5.ComputeHash( Encoding.UTF8.GetBytes( FullText ) );
|
|
Hash = BitConverter.ToUInt16( md5.Hash, 0 );
|
|
}
|
|
|
|
private void CompleteDefinition()
|
|
{
|
|
foreach ( Class c in Classes )
|
|
{
|
|
c.ClassIdent = FastHash( c.ManagedNameWithNamespace );
|
|
//
|
|
// This class has a baseclass, find the actual class and set it.
|
|
//
|
|
if ( !string.IsNullOrWhiteSpace( c.BaseClassName ) )
|
|
{
|
|
c.BaseClass = Classes.FirstOrDefault( x => x.ManagedNameWithNamespace == c.BaseClassName );
|
|
|
|
c.BaseClass ??= Classes.SingleOrDefault( x => x.ManagedName == c.BaseClassName );
|
|
|
|
if ( c.BaseClass == null )
|
|
{
|
|
throw new Exception( $"{c.ManagedNameWithNamespace} has unknown baseclass: {c.BaseClassName}" );
|
|
}
|
|
}
|
|
|
|
Class baseclass = c.BaseClass;
|
|
|
|
while ( baseclass != null )
|
|
{
|
|
c.ClassDepth++;
|
|
c.Functions.AddRange( baseclass.Functions.Where( x => !x.Static ) );
|
|
baseclass = baseclass.BaseClass;
|
|
}
|
|
|
|
//
|
|
// Fix the functions up
|
|
//
|
|
foreach ( Function f in c.Functions )
|
|
{
|
|
try
|
|
{
|
|
f.Return = TryFixArg( f.Return );
|
|
|
|
for ( int i = 0; i < f.Parameters.Length; i++ )
|
|
{
|
|
f.Parameters[i] = TryFixArg( f.Parameters[i] );
|
|
}
|
|
}
|
|
catch ( System.Exception e )
|
|
{
|
|
Log.Warning( $"[{e.Message}] in {c.ManagedNameWithNamespace}.{f.Name}" );
|
|
throw new Exception( $"Unhandled Type [{e.Message}] in {c.ManagedNameWithNamespace}.{f.Name}" );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Fix the variables up
|
|
//
|
|
foreach ( Variable f in c.Variables )
|
|
{
|
|
try
|
|
{
|
|
f.Return = TryFixArg( f.Return );
|
|
}
|
|
catch ( System.Exception e )
|
|
{
|
|
Log.Warning( $"[{e.Message}] in {c.ManagedNameWithNamespace}.{f.Name}" );
|
|
throw new Exception( "Unhandled Type" );
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ( Class c in Classes )
|
|
{
|
|
c.Functions = c.Functions.Distinct().Select( x => x.Copy() ).ToList();
|
|
c.Children = Classes.Where( x => x.DerivesFrom( c ) ).ToList();
|
|
}
|
|
}
|
|
|
|
private Arg TryFixArg( Arg arg )
|
|
{
|
|
if ( arg is ArgUnknown au )
|
|
{
|
|
Class c = Classes.SingleOrDefault( x => x.ManagedNameWithNamespace == au.Type );
|
|
if ( c != null && c.Native )
|
|
{
|
|
return ClassArgument( au, c );
|
|
}
|
|
|
|
if ( c != null && !c.Native )
|
|
{
|
|
return au.Wrap( new ArgManagedClass( c, au.Name, au.Flags ) );
|
|
}
|
|
|
|
Struct s = Structs.SingleOrDefault( x => x.ManagedNameWithNamespace == au.Type );
|
|
if ( s != null )
|
|
{
|
|
return au.Wrap( s.IsEnum ? new ArgEnum( s, au.Name ) : new ArgDefinedStruct( s, au.Name, au.Flags ) );
|
|
}
|
|
|
|
c = Classes.SingleOrDefault( x => x.ManagedName == au.Type );
|
|
if ( c != null && c.Native )
|
|
{
|
|
return ClassArgument( au, c );
|
|
}
|
|
|
|
if ( c != null && !c.Native )
|
|
{
|
|
return au.Wrap( new ArgManagedClass( c, au.Name, au.Flags ) );
|
|
}
|
|
|
|
s = Structs.SingleOrDefault( x => x.ManagedName == au.Type || x.NativeNameWithNamespace == au.Type || x.NativeName == au.Type );
|
|
return s != null
|
|
? au.Wrap( s.IsEnum ? new ArgEnum( s, au.Name ) : new ArgDefinedStruct( s, au.Name, au.Flags ) )
|
|
: FunctionPointers.Contains( au.Type )
|
|
? au.Wrap( new ArgFunctionPointer( au.Type, au.Name, au.Flags ) )
|
|
: Delegates.Contains( au.Type )
|
|
? au.Wrap( new ArgDelegate( au.Type, au.Name, au.Flags ) )
|
|
: throw new System.Exception( $"Unknown Type {au.Type}" );
|
|
}
|
|
|
|
return arg;
|
|
}
|
|
|
|
private Arg ClassArgument( ArgUnknown au, Class c )
|
|
{
|
|
return c.HasAttribute( "SharedDataPointer" )
|
|
? au.Wrap( new ArgSharedDataPointer( c, au.Name, au.Flags ) )
|
|
: au.Wrap( new ArgDefinedClass( c, au.Name, au.Flags ) );
|
|
}
|
|
|
|
private void Sort()
|
|
{
|
|
Structs = Structs.OrderBy( x => x.NativeName ).ToList();
|
|
Classes = Classes.OrderBy( x => x.NativeNameWithNamespace ).ToList();
|
|
|
|
foreach ( Class c in Classes )
|
|
{
|
|
c.Functions = c.Functions.OrderBy( x => x.MangledName ).ToList();
|
|
}
|
|
}
|
|
|
|
public bool IsIncluded( Class c )
|
|
{
|
|
return IncludedDefinitions.Any( x => x.Classes.Any( y => y.ManagedNameWithNamespace == c.ManagedNameWithNamespace && y.NativeNameWithNamespace == c.NativeNameWithNamespace ) );
|
|
}
|
|
|
|
public bool IsIncluded( Struct s )
|
|
{
|
|
return IncludedDefinitions.Any( x => x.Structs.Any( y => y.ManagedNameWithNamespace == s.ManagedNameWithNamespace && y.NativeNameWithNamespace == s.NativeNameWithNamespace ) );
|
|
}
|
|
|
|
public bool isRebind( Class c )
|
|
{
|
|
return SkipDefines.Any( x => x.Classes.Any( y => y.ManagedNameWithNamespace == c.ManagedNameWithNamespace && y.NativeNameWithNamespace == c.NativeNameWithNamespace ) );
|
|
}
|
|
|
|
public static int FastHash( string str )
|
|
{
|
|
// FNV-1a hash
|
|
uint hash = 0x811C9DC5;
|
|
byte[] data = Encoding.Unicode.GetBytes( str );
|
|
|
|
foreach ( byte b in data )
|
|
{
|
|
hash ^= b;
|
|
hash *= 0x1000193;
|
|
}
|
|
|
|
return unchecked((int)hash);
|
|
}
|
|
}
|