Facepunch.Steamworks/Generator/CodeWriter/Struct.cs

411 lines
15 KiB
C#
Raw Normal View History

2016-10-25 12:29:35 +03:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Generator
{
2016-10-25 18:11:29 +03:00
public partial class CodeWriter
2016-10-25 12:29:35 +03:00
{
2016-10-29 15:02:36 +03:00
public class TypeDef
{
public string Name;
public string NativeType;
public string ManagedType;
}
private Dictionary<string, TypeDef> TypeDefs = new Dictionary<string, TypeDef>();
2016-10-29 14:49:36 +03:00
//
// Don't give a fuck about these classes, they just cause us trouble
//
public readonly static string[] SkipStructs = new string[]
{
"CSteamID",
"CSteamAPIContext",
"CCallResult",
"CCallback",
2016-10-30 23:52:42 +03:00
"ValvePackingSentinel_t",
2019-04-11 18:37:51 +03:00
"CCallbackBase",
"CSteamGameServerAPIContext"
};
2016-10-29 14:49:36 +03:00
public readonly static string[] ForceLargePackStructs = new string[]
{
"LeaderboardEntry_t"
};
2016-10-25 12:29:35 +03:00
void Structs()
{
var callbackList = new List<SteamApiDefinition.StructDef>();
2016-10-25 12:29:35 +03:00
foreach ( var c in def.structs )
{
2016-10-29 14:49:36 +03:00
if ( SkipStructs.Contains( c.Name ) )
2016-10-25 12:29:35 +03:00
continue;
if ( c.Name.Contains( "::" ) )
continue;
int defaultPack = 8;
2018-03-21 13:42:55 +03:00
if ( c.Fields.Any( x => x.Type.Contains( "class CSteamID" ) ) && !ForceLargePackStructs.Contains( c.Name ) )
2016-10-25 13:51:24 +03:00
defaultPack = 4;
2016-10-25 12:29:35 +03:00
var isCallback = !string.IsNullOrEmpty( c.CallbackId );
//
// Main struct
//
StartBlock( $"public struct {c.Name}{(isCallback?" : Steamworks.ISteamCallback":"")}" );
2016-10-25 12:29:35 +03:00
{
if ( isCallback )
2016-10-29 22:28:16 +03:00
{
2018-03-21 13:42:55 +03:00
WriteLine( "internal const int CallbackId = " + c.CallbackId + ";" );
WriteLine( "public int GetCallbackId() => CallbackId;" );
WriteLine( "public int GetStructSize() => StructSize();" );
StartBlock( "public Steamworks.ISteamCallback Fill( IntPtr p, int size)" );
{
WriteLine( "return FromPointer( p ); // TODO - USE SIZE HERE SOMEHOW" );
}
EndBlock();
}
2016-10-29 22:28:16 +03:00
2016-10-29 14:49:36 +03:00
//
// The fields
//
StructFields( c.Fields );
WriteLine();
WriteLine( "//" );
2016-10-31 14:46:53 +03:00
WriteLine( "// Read this struct from a pointer, usually from Native. It will automatically do the awesome stuff." );
2016-10-29 14:49:36 +03:00
WriteLine( "//" );
WriteLine( $"internal static {c.Name} FromPointer( IntPtr p ) => " );
2016-10-29 14:49:36 +03:00
{
WriteLine( $" Platform.PackSmall ? (({c.Name})(Pack4) Marshal.PtrToStructure( p, typeof(Pack4) )) : (({c.Name})(Pack8) Marshal.PtrToStructure( p, typeof(Pack8) ));" );
2016-10-29 14:49:36 +03:00
}
2018-02-14 17:15:02 +03:00
WriteLine();
WriteLine( "//" );
WriteLine( "// Get the size of the structure we're going to be using." );
WriteLine( "//" );
StartBlock( $"internal static int StructSize()" );
{
WriteLine( $"return System.Runtime.InteropServices.Marshal.SizeOf( Platform.PackSmall ? typeof(Pack4) : typeof(Pack8) );" );
2018-02-14 17:15:02 +03:00
}
EndBlock();
// if ( defaultPack == 8 )
// defaultPack = 4;
2016-10-29 14:49:36 +03:00
//
// Small packed struct (for osx, linux)
//
WriteLine();
WriteLine( $"[StructLayout( LayoutKind.Sequential, Pack = 4 )]" );
StartBlock( $"public struct Pack4" );
2016-10-29 14:49:36 +03:00
{
StructFields( c.Fields );
//
// Implicit convert from PackSmall to regular
//
WriteLine();
Write( $"public static implicit operator {c.Name} ( {c.Name}.Pack4 d ) => " );
2016-10-29 14:49:36 +03:00
{
Write( $"new {c.Name}{{ " );
2016-10-29 14:49:36 +03:00
{
foreach ( var f in c.Fields )
{
Write( $"{CleanMemberName( f.Name )} = d.{CleanMemberName( f.Name )}," );
2016-10-29 14:49:36 +03:00
}
}
WriteLine( " };" );
2016-10-29 14:49:36 +03:00
}
}
2016-10-29 14:49:36 +03:00
EndBlock();
2016-10-25 12:29:35 +03:00
//
// Small packed struct (for osx, linux)
//
WriteLine();
WriteLine( $"[StructLayout( LayoutKind.Sequential, Pack = {defaultPack} )]" );
StartBlock( $"public struct Pack8" );
{
StructFields( c.Fields );
//
// Implicit convert from PackSmall to regular
//
WriteLine();
Write( $"public static implicit operator {c.Name} ( {c.Name}.Pack8 d ) => " );
{
Write( $"new {c.Name}{{ " );
{
foreach ( var f in c.Fields )
{
Write( $"{CleanMemberName( f.Name )} = d.{CleanMemberName( f.Name )}," );
}
}
WriteLine( " };" );
}
}
EndBlock();
if ( c.IsCallResult )
2016-10-31 14:46:53 +03:00
{
CallResult( c );
}
2017-05-08 22:51:57 +03:00
if ( !string.IsNullOrEmpty( c.CallbackId ) )
2016-10-30 23:52:42 +03:00
{
Callback( c );
callbackList.Add( c );
2016-10-30 23:52:42 +03:00
}
2016-10-29 14:49:36 +03:00
}
2016-10-25 12:29:35 +03:00
EndBlock();
WriteLine();
}
StartBlock( $"internal static class Callbacks" );
StartBlock( $"internal static void RegisterCallbacks( Facepunch.Steamworks.BaseSteamworks steamworks )" );
{
foreach ( var c in callbackList )
{
WriteLine( $"{c.Name}.Register( steamworks );" );
}
}
EndBlock();
EndBlock();
2016-10-25 12:29:35 +03:00
}
private void StructFields( SteamApiDefinition.StructDef.StructFields[] fields )
{
foreach ( var m in fields )
{
var t = ToManagedType( m.Type );
if ( TypeDefs.ContainsKey( t ) )
{
t = TypeDefs[t].ManagedType;
}
2016-10-25 13:51:24 +03:00
if ( t == "bool" )
{
WriteLine( "[MarshalAs(UnmanagedType.I1)]" );
}
2016-10-25 12:29:35 +03:00
if ( t.StartsWith( "char " ) && t.Contains( "[" ) )
{
var num = t.Replace( "char", "" ).Trim( '[', ']', ' ' );
t = "string";
WriteLine( $"[MarshalAs(UnmanagedType.ByValTStr, SizeConst = {num})]" );
2016-10-25 12:29:35 +03:00
}
2016-10-26 18:10:20 +03:00
if ( t.StartsWith( "uint8 " ) && t.Contains( "[" ) )
{
var num = t.Replace( "uint8", "" ).Trim( '[', ']', ' ' );
t = "byte[]";
WriteLine( $"[MarshalAs(UnmanagedType.ByValArray, SizeConst = {num})] // {m.Name}" );
2016-10-26 18:10:20 +03:00
}
2016-10-25 12:29:35 +03:00
if ( t.StartsWith( "CSteamID " ) && t.Contains( "[" ) )
{
var num = t.Replace( "CSteamID", "" ).Trim( '[', ']', ' ' );
t = $"ulong[]";
WriteLine( $"[MarshalAs(UnmanagedType.ByValArray, SizeConst = {num}, ArraySubType = UnmanagedType.U8)]" );
}
if ( t.StartsWith( "PublishedFileId_t " ) && t.Contains( "[" ) )
{
var num = t.Replace( "PublishedFileId_t", "" ).Trim( '[', ']', ' ' );
t = $"ulong[]";
WriteLine( $"[MarshalAs(UnmanagedType.ByValArray, SizeConst = {num}, ArraySubType = UnmanagedType.U8)]" );
}
if ( t.StartsWith( "uint32 " ) && t.Contains( "[" ) )
{
var num = t.Replace( "uint32", "" ).Trim( '[', ']', ' ' );
t = $"uint[]";
WriteLine( $"[MarshalAs(UnmanagedType.ByValArray, SizeConst = {num}, ArraySubType = UnmanagedType.U4)]" );
2016-10-25 12:29:35 +03:00
}
if ( t.StartsWith( "float " ) && t.Contains( "[" ) )
{
var num = t.Replace( "float", "" ).Trim( '[', ']', ' ' );
t = $"float[]";
WriteLine( $"[MarshalAs(UnmanagedType.ByValArray, SizeConst = {num}, ArraySubType = UnmanagedType.R4)]" );
}
if ( t == "const char **" )
{
t = "IntPtr";
}
2018-01-23 14:00:14 +03:00
if (t.StartsWith("AppId_t ") && t.Contains("["))
{
var num = t.Replace("AppId_t", "").Trim('[', ']', ' ');
t = $"AppId_t[]";
WriteLine($"[MarshalAs(UnmanagedType.ByValArray, SizeConst = {num}, ArraySubType = UnmanagedType.U4)]");
}
2018-02-14 17:15:02 +03:00
WriteLine( $"internal {t} {CleanMemberName( m.Name )}; // {m.Name} {m.Type}" );
2016-10-25 12:29:35 +03:00
}
}
2016-10-30 23:52:42 +03:00
private void Callback( SteamApiDefinition.StructDef c )
{
2016-10-30 23:52:42 +03:00
WriteLine();
StartBlock( $"internal static void Register( Facepunch.Steamworks.BaseSteamworks steamworks )" );
2016-10-30 23:52:42 +03:00
{
2018-02-14 17:15:02 +03:00
WriteLine( $"var handle = new CallbackHandle( steamworks );" );
2016-10-30 23:52:42 +03:00
WriteLine( $"" );
CallbackCall( c );
2016-10-30 23:52:42 +03:00
WriteLine( "" );
WriteLine( "//" );
WriteLine( "// Register the callback with Steam" );
WriteLine( "//" );
WriteLine( $"steamworks.native.api.SteamAPI_RegisterCallback( handle.PinnedCallback.AddrOfPinnedObject(), CallbackId );" );
WriteLine();
WriteLine( "steamworks.RegisterCallbackHandle( handle );" );
}
EndBlock();
WriteLine();
WriteLine( "[MonoPInvokeCallback]" );
WriteLine( "internal static void OnResultThis( IntPtr self, IntPtr param ){ OnResult( param ); }" );
WriteLine( "[MonoPInvokeCallback]" );
WriteLine( "internal static void OnResultWithInfoThis( IntPtr self, IntPtr param, bool failure, SteamNative.SteamAPICall_t call ){ OnResultWithInfo( param, failure, call ); }" );
WriteLine( "[MonoPInvokeCallback]" );
WriteLine( "internal static int OnGetSizeThis( IntPtr self ){ return OnGetSize(); }" );
WriteLine( "[MonoPInvokeCallback]" );
WriteLine( "internal static int OnGetSize(){ return StructSize(); }" );
WriteLine();
WriteLine( "[MonoPInvokeCallback]" );
StartBlock( "internal static void OnResult( IntPtr param )" );
{
WriteLine( $"OnResultWithInfo( param, false, 0 );" );
}
EndBlock();
WriteLine();
WriteLine( "[MonoPInvokeCallback]" );
StartBlock( "internal static void OnResultWithInfo( IntPtr param, bool failure, SteamNative.SteamAPICall_t call )" );
{
WriteLine( $"if ( failure ) return;" );
WriteLine();
WriteLine( "var value = FromPointer( param );" );
WriteLine();
WriteLine( "if ( Facepunch.Steamworks.Client.Instance != null )" );
WriteLine( $" Facepunch.Steamworks.Client.Instance.OnCallback<{c.Name}>( value );" );
WriteLine();
WriteLine( "if ( Facepunch.Steamworks.Server.Instance != null )" );
WriteLine( $" Facepunch.Steamworks.Server.Instance.OnCallback<{c.Name}>( value );" );
}
EndBlock();
2016-10-30 23:52:42 +03:00
}
2016-10-31 14:46:53 +03:00
2016-11-01 16:37:47 +03:00
2016-10-31 14:46:53 +03:00
private void CallResult( SteamApiDefinition.StructDef c )
{
WriteLine();
2018-02-14 17:15:02 +03:00
StartBlock( $"internal static CallResult<{c.Name}> CallResult( Facepunch.Steamworks.BaseSteamworks steamworks, SteamAPICall_t call, Action<{c.Name}, bool> CallbackFunction )" );
2016-10-31 14:46:53 +03:00
{
2018-02-14 17:15:02 +03:00
WriteLine( $"return new CallResult<{c.Name}>( steamworks, call, CallbackFunction, FromPointer, StructSize(), CallbackId );" );
2016-11-01 16:37:47 +03:00
}
EndBlock();
}
private void CallbackCall( SteamApiDefinition.StructDef c )
2016-11-01 16:37:47 +03:00
{
WriteLine( "//" );
WriteLine( "// Create the functions we need for the vtable" );
WriteLine( "//" );
2017-02-01 15:44:00 +03:00
StartBlock( "if ( Facepunch.Steamworks.Config.UseThisCall )" );
{
CallFunctions( c, "ThisCall", "_" );
2017-02-01 15:44:00 +03:00
}
Else();
{
CallFunctions( c, "StdCall", "" );
2017-02-01 15:44:00 +03:00
}
EndBlock();
WriteLine( "" );
WriteLine( "//" );
WriteLine( "// Create the callback object" );
WriteLine( "//" );
WriteLine( $"var cb = new Callback();" );
WriteLine( $"cb.vTablePtr = handle.vTablePtr;" );
WriteLine( $"cb.CallbackFlags = steamworks.IsGameServer ? (byte) SteamNative.Callback.Flags.GameServer : (byte) 0;" );
WriteLine( $"cb.CallbackId = CallbackId;" );
WriteLine( "" );
WriteLine( "//" );
WriteLine( "// Pin the callback, so it doesn't get garbage collected and we can pass the pointer to native" );
WriteLine( "//" );
WriteLine( $"handle.PinnedCallback = GCHandle.Alloc( cb, GCHandleType.Pinned );" );
}
private void CallFunctions( SteamApiDefinition.StructDef c, string ThisCall, string ThisArg )
2017-02-01 15:44:00 +03:00
{
var ThisArgC = ThisArg.Length > 0 ? $"{ThisArg}, " : "";
var This = ThisArg.Length > 0 ? "This" : "";
2017-02-01 15:44:00 +03:00
WriteLine( "//" );
WriteLine( "// Create the VTable by manually allocating the memory and copying across" );
WriteLine( "//" );
StartBlock( "if ( Platform.IsWindows )" );
2016-11-01 16:37:47 +03:00
{
WriteLine( $"handle.vTablePtr = Marshal.AllocHGlobal( Marshal.SizeOf( typeof( Callback.VTableWin{This} ) ) );" );
StartBlock( $"var vTable = new Callback.VTableWin{This}" );
2016-10-31 15:44:37 +03:00
{
WriteLine( $"ResultA = OnResult{This}," );
WriteLine( $"ResultB = OnResultWithInfo{This}," );
WriteLine( $"GetSize = OnGetSize{This}," );
2016-10-31 15:44:37 +03:00
}
EndBlock( ";" );
2016-10-31 14:46:53 +03:00
WriteLine( "handle.FuncA = GCHandle.Alloc( vTable.ResultA );" );
WriteLine( "handle.FuncB = GCHandle.Alloc( vTable.ResultB );" );
WriteLine( "handle.FuncC = GCHandle.Alloc( vTable.GetSize );" );
2016-10-31 14:46:53 +03:00
WriteLine( "Marshal.StructureToPtr( vTable, handle.vTablePtr, false );" );
2016-11-01 16:37:47 +03:00
}
Else();
2016-11-01 16:37:47 +03:00
{
WriteLine( $"handle.vTablePtr = Marshal.AllocHGlobal( Marshal.SizeOf( typeof( Callback.VTable{This} ) ) );" );
StartBlock( $"var vTable = new Callback.VTable{This}" );
{
WriteLine( $"ResultA = OnResult{This}," );
WriteLine( $"ResultB = OnResultWithInfo{This}," );
WriteLine( $"GetSize = OnGetSize{This}," );
}
EndBlock( ";" );
WriteLine( "handle.FuncA = GCHandle.Alloc( vTable.ResultA );" );
WriteLine( "handle.FuncB = GCHandle.Alloc( vTable.ResultB );" );
WriteLine( "handle.FuncC = GCHandle.Alloc( vTable.GetSize );" );
WriteLine( "Marshal.StructureToPtr( vTable, handle.vTablePtr, false );" );
2016-10-31 14:46:53 +03:00
}
EndBlock();
2016-11-01 16:37:47 +03:00
2016-10-31 14:46:53 +03:00
}
2016-10-25 12:29:35 +03:00
}
}