Facepunch.Steamworks/Generator/CodeWriter/ClassVTable.cs

314 lines
8.4 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Generator
{
public partial class CodeWriter
{
public void GenerateVTableClass( string className, string filename )
{
var clss = Parser.Classes.Single( x => x.Name == className );
sb = new StringBuilder();
WriteLine( $"using System;" );
WriteLine( $"using System.Runtime.InteropServices;" );
WriteLine( $"using System.Text;" );
WriteLine( $"using System.Threading.Tasks;" );
2019-04-16 16:38:10 +03:00
WriteLine( $"using Steamworks.Data;" );
WriteLine();
WriteLine();
2019-04-16 16:38:10 +03:00
StartBlock( $"namespace Steamworks" );
{
StartBlock( $"internal class {clss.Name} : SteamInterface" );
{
2019-04-13 00:53:46 +03:00
WriteLine( $"public override string InterfaceName => \"{clss.InterfaceString}\";" );
WriteLine();
WriteFunctionPointerReader( clss );
WriteLine();
foreach ( var func in clss.Functions )
{
2019-04-27 16:25:54 +03:00
if ( Cleanup.IsDeprecated( $"{clss.Name}.{func.Name}" ) )
continue;
WriteFunction( clss, func );
WriteLine();
}
}
EndBlock();
}
EndBlock();
System.IO.File.WriteAllText( $"{filename}", sb.ToString() );
}
void WriteFunctionPointerReader( CodeParser.Class clss )
{
2019-04-17 15:53:54 +03:00
// TODO - we'll probably have to do this PER platform
int[] standardLocations = new int[clss.Functions.Count];
int[] windowsLocations = new int[clss.Functions.Count];
2019-04-17 15:53:54 +03:00
for ( int i = 0; i < clss.Functions.Count; i++ )
{
windowsLocations[i] = i * 8;
standardLocations[i] = i * 8;
2019-04-17 15:53:54 +03:00
}
//
// MSVC switches the order in the vtable of overloaded functions
// I'm not going to try to try to work out how to order shit
// so lets just manually fix shit here
//
if ( clss.Name == "ISteamUserStats" )
{
Swap( clss, "GetStat1", "GetStat2", windowsLocations );
Swap( clss, "SetStat1", "SetStat2", windowsLocations );
Swap( clss, "GetUserStat1", "GetUserStat2", windowsLocations );
Swap( clss, "GetGlobalStat1", "GetGlobalStat2", windowsLocations );
Swap( clss, "GetGlobalStatHistory1", "GetGlobalStatHistory2", windowsLocations );
2019-04-17 15:53:54 +03:00
}
if ( clss.Name == "ISteamGameServerStats" )
{
Swap( clss, "GetUserStat1", "GetUserStat2", windowsLocations );
Swap( clss, "SetUserStat1", "SetUserStat2", windowsLocations );
}
2019-04-17 18:41:06 +03:00
if ( clss.Name == "ISteamUGC" )
{
Swap( clss, "CreateQueryAllUGCRequest1", "CreateQueryAllUGCRequest2", windowsLocations );
2019-04-17 18:41:06 +03:00
}
StartBlock( $"public override void InitInternals()" );
{
2019-06-25 14:04:43 +03:00
var different = new List<int>();
2019-04-27 16:25:54 +03:00
2019-06-25 14:04:43 +03:00
for ( int i = 0; i < clss.Functions.Count; i++ )
{
2019-06-25 14:04:43 +03:00
var func = clss.Functions[i];
if ( Cleanup.IsDeprecated( $"{clss.Name}.{func.Name}" ) )
2019-04-27 16:25:54 +03:00
{
2019-06-25 14:04:43 +03:00
WriteLine( $" // {func.Name} is deprecated" );
}
else
{
if ( standardLocations[i] != windowsLocations[i] )
{
different.Add( i );
continue;
}
2019-06-25 14:40:23 +03:00
WriteLine( $"_{func.Name} = Marshal.GetDelegateForFunctionPointer<F{func.Name}>( Marshal.ReadIntPtr( VTable, Platform.MemoryOffset( {standardLocations[i]} ) ) );" );
2019-04-27 16:25:54 +03:00
}
}
2019-06-25 14:04:43 +03:00
if ( different.Count > 0 )
{
2019-06-25 14:04:43 +03:00
WriteLine( "" );
WriteLine( "#if PLATFORM_WIN" );
2019-06-25 14:04:43 +03:00
foreach ( var i in different )
{
var func = clss.Functions[i];
2019-06-25 14:40:23 +03:00
WriteLine( $"_{func.Name} = Marshal.GetDelegateForFunctionPointer<F{func.Name}>( Marshal.ReadIntPtr( VTable, Platform.MemoryOffset( {windowsLocations[i]} ) ) );" );
2019-06-25 14:04:43 +03:00
}
WriteLine( "#else" );
2019-06-25 14:04:43 +03:00
foreach ( var i in different )
{
var func = clss.Functions[i];
2019-06-25 14:40:23 +03:00
WriteLine( $"_{func.Name} = Marshal.GetDelegateForFunctionPointer<F{func.Name}>( Marshal.ReadIntPtr( VTable, Platform.MemoryOffset( {standardLocations[i]} ) ) );" );
2019-04-27 16:25:54 +03:00
}
2019-06-25 14:04:43 +03:00
WriteLine( "#endif" );
}
}
EndBlock();
StartBlock( $"internal override void Shutdown()" );
{
WriteLine( $"base.Shutdown();" );
WriteLine( "" );
for ( int i = 0; i < clss.Functions.Count; i++ )
{
var func = clss.Functions[i];
var returnType = BaseType.Parse( func.ReturnType );
var args = func.Arguments.Select( x => BaseType.Parse( x.Value, x.Key ) ).ToArray();
var windowsSpecific = NeedsWindowsSpecificFunction( func, returnType, args );
if ( Cleanup.IsDeprecated( $"{clss.Name}.{func.Name}" ) )
continue;
WriteLine( $"_{func.Name} = null;" );
}
}
EndBlock();
}
private bool NeedsWindowsSpecificFunction( CodeParser.Class.Function func, BaseType returnType, BaseType[] args )
{
if ( returnType.IsReturnedWeird ) return true;
if ( returnType.WindowsSpecific ) return true;
if ( args.Any( x => x.WindowsSpecific ) ) return true;
return false;
}
2019-04-17 15:53:54 +03:00
private void Swap( CodeParser.Class clss, string v1, string v2, int[] locations )
{
var a = clss.Functions.IndexOf( clss.Functions.Single( x => x.Name == v1 ) );
var b = clss.Functions.IndexOf( clss.Functions.Single( x => x.Name == v2 ) );
var s = locations[a];
locations[a] = locations[b];
locations[b] = s;
}
private void WriteFunction( CodeParser.Class clss, CodeParser.Class.Function func )
{
var returnType = BaseType.Parse( func.ReturnType );
2019-04-17 15:20:32 +03:00
returnType.Func = func.Name;
2019-04-17 15:20:32 +03:00
var args = func.Arguments.Select( x =>
{
var bt = BaseType.Parse( x.Value, x.Key );
bt.Func = func.Name;
return bt;
} ).ToArray();
for( int i=0; i<args.Length; i++ )
{
if ( args[i] is StringType )
{
if ( args[i + 1] is IntType || args[i + 1] is UIntType )
{
if ( args[i + 1].Ref == string.Empty )
{
args[i + 1] = new ConstValueType( args[i + 1], "(1024 * 32)" );
}
}
else
{
throw new System.Exception( $"String Builder Next Type Is {args[i+1].GetType()}" );
}
}
}
var argstr = string.Join( ", ", args.Where( x => !x.ShouldSkipAsArgument ).Select( x => x.AsArgument() ) ); ;
var delegateargstr = string.Join( ", ", args.Select( x => x.AsNativeArgument() ) );
var windowsSpecific = NeedsWindowsSpecificFunction( func, returnType, args );
if ( returnType is SteamApiCallType sap )
{
sap.CallResult = func.CallResult;
2019-05-01 23:33:10 +03:00
argstr = string.Join( ", ", args.Select( x => x.AsArgument().Replace( "ref ", " /* ref */ " ) ) );
}
WriteLine( $"#region FunctionMeta" );
WriteLine( $"[UnmanagedFunctionPointer( Platform.MemberConvention )]" );
if ( returnType.ReturnAttribute != null)
WriteLine( returnType.ReturnAttribute );
if ( returnType.IsReturnedWeird )
{
WriteLine( "#if PLATFORM_WIN" );
WriteLine( $"private delegate void F{func.Name}( IntPtr self, ref {returnType.TypeName} retVal, {delegateargstr} );".Replace( " retVal, )", " retVal )" ) );
WriteLine( "#else" );
}
WriteLine( $"private delegate {returnType.TypeNameFrom} F{func.Name}( IntPtr self, {delegateargstr} );".Replace( "( IntPtr self, )", "( IntPtr self )" ) );
if ( returnType.IsReturnedWeird )
{
WriteLine( "#endif" );
}
WriteLine( $"private F{func.Name} _{func.Name};" );
WriteLine();
WriteLine( $"#endregion" );
2019-04-16 14:17:24 +03:00
StartBlock( $"internal {returnType.ReturnType} {func.Name}( {argstr} )".Replace( "( )", "()" ) );
{
var callargs = string.Join( ", ", args.Select( x => x.AsCallArgument() ) );
//
// Code before any calls
//
foreach ( var arg in args )
{
if ( arg is StringType sb )
{
WriteLine( $"IntPtr mem{sb.VarName} = Helpers.TakeMemory();" );
}
}
//
// The actual call
//
if ( returnType.IsReturnedWeird )
{
WriteLine( "#if PLATFORM_WIN" );
{
WriteLine( $"var retVal = default( {returnType.TypeName} );" );
WriteLine( $"_{func.Name}( Self, ref retVal, {callargs} );".Replace( ", );", " );" ) );
WriteLine( $"{returnType.Return( "retVal" )}" );
}
WriteLine( "#else" );
}
if ( returnType.IsVoid )
{
2019-04-17 15:36:11 +03:00
WriteLine( $"_{func.Name}( Self, {callargs} );".Replace( "( Self, )", "( Self )" ) );
}
else
{
WriteLine( $"var returnValue = _{func.Name}( Self, {callargs} );".Replace( "( Self, )", "( Self )" ) );
}
//
// Code after the call
//
foreach ( var arg in args )
{
if ( arg is StringType sb )
{
WriteLine( $"{sb.VarName} = Helpers.MemoryToString( mem{sb.VarName} );" );
}
}
//
// Return
//
if ( !returnType.IsVoid )
{
WriteLine( returnType.Return( "returnValue" ) );
}
if ( returnType.IsReturnedWeird )
{
WriteLine( "#endif" );
}
}
EndBlock();
}
}
}