From f562e651491d7e01c4c1abb110fd376055861c02 Mon Sep 17 00:00:00 2001 From: Garry Newman Date: Thu, 20 Feb 2020 12:09:23 +0000 Subject: [PATCH] Cleanup --- .../Interfaces/ISteamMatchmakingServers.cs | 2 +- .../Interfaces/ISteamNetworkingUtils.cs | 2 +- Generator/Cleanup.cs | 12 ++ Generator/CodeWriter/CodeWriter.cs | 35 ++++ Generator/CodeWriter/Interface.cs | 8 +- Generator/Generator.csproj | 9 +- Generator/{CodeWriter => }/Types/BaseType.cs | 181 +++--------------- Generator/Types/BoolType.cs | 20 ++ Generator/Types/ConstCharType.cs | 21 ++ Generator/Types/FetchStringType.cs | 25 +++ Generator/Types/LiteralType.cs | 32 ++++ Generator/Types/SteamApiCallType.cs | 34 ++++ Generator/Types/StructType.cs | 31 +++ Generator/Types/VoidType.cs | 17 ++ 14 files changed, 268 insertions(+), 161 deletions(-) rename Generator/{CodeWriter => }/Types/BaseType.cs (50%) create mode 100644 Generator/Types/BoolType.cs create mode 100644 Generator/Types/ConstCharType.cs create mode 100644 Generator/Types/FetchStringType.cs create mode 100644 Generator/Types/LiteralType.cs create mode 100644 Generator/Types/SteamApiCallType.cs create mode 100644 Generator/Types/StructType.cs create mode 100644 Generator/Types/VoidType.cs diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServers.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServers.cs index 81c7150..b1740fa 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServers.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServers.cs @@ -104,7 +104,7 @@ namespace Steamworks internal gameserveritem_t GetServerDetails( HServerListRequest hRequest, int iServer ) { var returnValue = _GetServerDetails( Self, hRequest, iServer ); - return (gameserveritem_t) Marshal.PtrToStructure( returnValue, typeof( gameserveritem_t ) ); + return returnValue.ToType(); } #region FunctionMeta diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamNetworkingUtils.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamNetworkingUtils.cs index 4b5d867..e18452a 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamNetworkingUtils.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamNetworkingUtils.cs @@ -28,7 +28,7 @@ namespace Steamworks internal NetMsg AllocateMessage( int cbAllocateBuffer ) { var returnValue = _AllocateMessage( Self, cbAllocateBuffer ); - return (NetMsg) Marshal.PtrToStructure( returnValue, typeof( NetMsg ) ); + return returnValue.ToType(); } #region FunctionMeta diff --git a/Generator/Cleanup.cs b/Generator/Cleanup.cs index f9cbc6b..bbd3757 100644 --- a/Generator/Cleanup.cs +++ b/Generator/Cleanup.cs @@ -144,5 +144,17 @@ public static class Cleanup } return false; + } + + // + // If we start with E[Capital] then strip the E + // (makes us more C# like) + // + internal static string CleanEnum( string name ) + { + if ( name[0] != 'E' ) return name; + if ( !char.IsUpper( name[1] ) ) return name; + + return name.Substring( 1 ); } } diff --git a/Generator/CodeWriter/CodeWriter.cs b/Generator/CodeWriter/CodeWriter.cs index c1ef9e1..c4d79b3 100644 --- a/Generator/CodeWriter/CodeWriter.cs +++ b/Generator/CodeWriter/CodeWriter.cs @@ -10,11 +10,14 @@ namespace Generator { public partial class CodeWriter { + public static CodeWriter Current { get; private set; } + private SteamApiDefinition def; public CodeWriter( SteamApiDefinition def ) { this.def = def; + Current = this; WorkoutTypes(); } @@ -119,5 +122,37 @@ namespace Generator { EndBlock(); } + + public bool IsStruct( string name ) + { + if ( def.structs.Any( x => x.Name == name || Cleanup.ConvertType( x.Name ) == name ) ) + return true; + + return false; + } + + public bool IsTypeDef( string name ) + { + if ( def.typedefs.Any( x => x.Name == name || Cleanup.ConvertType( x.Name ) == name ) ) + return true; + + return false; + } + + public bool IsCallback( string name ) + { + if ( def.callback_structs.Any( x => x.Name == name || Cleanup.ConvertType( x.Name ) == name ) ) + return true; + + return false; + } + + public bool IsEnum( string name ) + { + if ( def.enums.Any( x => x.Name == name || Cleanup.ConvertType( x.Name ) == name ) ) + return true; + + return false; + } } } diff --git a/Generator/CodeWriter/Interface.cs b/Generator/CodeWriter/Interface.cs index 913fd21..c35d22f 100644 --- a/Generator/CodeWriter/Interface.cs +++ b/Generator/CodeWriter/Interface.cs @@ -96,13 +96,13 @@ namespace Generator for( int i=0; i - + + + + + + + + diff --git a/Generator/CodeWriter/Types/BaseType.cs b/Generator/Types/BaseType.cs similarity index 50% rename from Generator/CodeWriter/Types/BaseType.cs rename to Generator/Types/BaseType.cs index 0ac003e..426400c 100644 --- a/Generator/CodeWriter/Types/BaseType.cs +++ b/Generator/Types/BaseType.cs @@ -13,8 +13,6 @@ internal class BaseType public string Func; - public virtual bool WindowsSpecific => false; - public static BaseType Parse( string type, string varname = null, string callresult = null ) { type = Cleanup.ConvertType( type ); @@ -26,7 +24,7 @@ internal class BaseType if ( type == "void" ) return new VoidType { NativeType = type, VarName = varname }; if ( type.Replace( " ", "" ).StartsWith( "constchar*" ) ) return new ConstCharType { NativeType = type, VarName = varname }; - if ( type == "char *" ) return new StringType { NativeType = type, VarName = varname }; + if ( type == "char *" ) return new FetchStringType { NativeType = type, VarName = varname }; var basicType = type.Replace( "const ", "" ).Trim( ' ', '*', '&' ); @@ -38,34 +36,39 @@ internal class BaseType if ( basicType == "uint8" ) return new UInt8Type { NativeType = type, VarName = varname }; if ( basicType == "uint16" ) return new UInt16Type { NativeType = type, VarName = varname }; if ( basicType == "unsigned short" ) return new UInt16Type { NativeType = type, VarName = varname }; - if ( basicType == "SteamId" ) return new CSteamIdType { NativeType = type, VarName = varname }; - - - if ( basicType == "size_t" ) return new UIntPtrType{ NativeType = type, VarName = varname }; - + // DANGER DANGER Danger - if ( basicType == "intptr_t" ) return new LongType { NativeType = type, VarName = varname }; - if ( basicType == "ptrdiff_t" ) return new LongType { NativeType = type, VarName = varname }; + if ( basicType == "size_t" ) return new UIntPtrType { NativeType = type, VarName = varname }; if ( basicType == "uint64" ) return new ULongType { NativeType = type, VarName = varname }; if ( basicType == "int64" ) return new LongType { NativeType = type, VarName = varname }; if ( basicType == "bool" ) return new BoolType { NativeType = type, VarName = varname }; - if ( basicType == "InventoryItemId" ) return new StructType { NativeType = type, VarName = varname, StructName = basicType }; - if ( basicType == "InventoryDefId" ) return new StructType { NativeType = type, VarName = varname, StructName = basicType }; - if ( basicType == "PingLocation" ) return new StructType { NativeType = type, VarName = varname, StructName = basicType }; - if ( basicType == "NetIdentity" ) return new StructType { NativeType = type, VarName = varname, StructName = basicType }; - if ( basicType == "NetAddress" ) return new StructType { NativeType = type, VarName = varname, StructName = basicType }; - if ( basicType == "ConnectionInfo" ) return new StructType { NativeType = type, VarName = varname, StructName = basicType }; - if ( basicType == "DigitalState" ) return new StructType { NativeType = type, VarName = varname, StructName = basicType }; - if ( basicType == "AnalogState" ) return new StructType { NativeType = type, VarName = varname, StructName = basicType }; - if ( basicType == "MotionState" ) return new StructType { NativeType = type, VarName = varname, StructName = basicType }; - if ( basicType == "NetMsg" ) return new StructType { NativeType = type, VarName = varname, StructName = basicType }; - if ( basicType == "NetKeyValue" ) return new StructType { NativeType = type, VarName = varname, StructName = basicType }; - if ( basicType == "SteamIPAddress" ) return new StructType { NativeType = type, VarName = varname, StructName = basicType }; - if ( basicType.StartsWith( "E" ) && char.IsUpper( basicType[1] ) ) return new EnumType { NativeType = type.Substring( 1 ), VarName = varname }; - if ( basicType.EndsWith( "_t" ) ) return new StructType { NativeType = type, VarName = varname, StructName = basicType }; + // + // Enum types are handled in a generic way, but we do need to clean up the name + // + if ( Generator.CodeWriter.Current.IsEnum( basicType ) ) + { + return new BaseType { NativeType = Cleanup.CleanEnum( type ), VarName = varname }; + } + + // + // Structs are generally sent as plain old data, but need marshalling if they're expected as a pointer + // + if ( Generator.CodeWriter.Current.IsStruct( basicType ) ) + { + return new StructType { NativeType = type, VarName = varname, StructName = basicType }; + } + + // + // c# doesn't really have typerdefs, so we convert things like HSteamUser into structs + // which from a memory point of view behave in the same way. + // + if ( Generator.CodeWriter.Current.IsTypeDef( basicType ) ) + { + return new StructType { NativeType = type, VarName = varname, StructName = basicType }; + } return new BaseType { NativeType = type, VarName = varname }; } @@ -123,85 +126,6 @@ internal class BaseType public virtual bool IsVoid => false; - public virtual bool IsReturnedWeird => false; -} - - -internal class BoolType : BaseType -{ - public override string TypeName => $"bool"; - public override string AsArgument() => $"[MarshalAs( UnmanagedType.U1 )] {Ref}{TypeName} {VarName}"; - public override string ReturnAttribute => "[return: MarshalAs( UnmanagedType.I1 )]"; - -} - -internal class StructType : BaseType -{ - public string StructName; - - public override string TypeName => StructName; - - public override string TypeNameFrom => NativeType.EndsWith( "*" ) ? "IntPtr" : base.ReturnType; - - public override string Return( string varname ) - { - if ( NativeType.EndsWith( "*" ) ) - { - return $"return ({TypeName}) Marshal.PtrToStructure( {varname}, typeof( {TypeName} ) );"; - } - - return base.Return( varname ); - } - - public override bool WindowsSpecific - { - get - { - var s = Generator.Program.Definitions.structs.FirstOrDefault( x => x.Name == StructName ); - if ( s == null ) return false; - - return !s.IsPack4OnWindows; - } - } - - - static string[] SpecialTypes = new string[] - { - "DigitalState", - "AnalogState", - "MotionState", - }; - - public override bool IsReturnedWeird => SpecialTypes.Contains( StructName ); -} - -internal class SteamApiCallType : BaseType -{ - public string CallResult; - public override string TypeName => "SteamAPICall_t"; - public override string Return( string varname ) - { - if ( !string.IsNullOrEmpty( CallResult ) ) - return $"return new CallResult<{CallResult}>( {varname} );"; - - return $"return new CallResult( {varname} );"; - } - - public override string ReturnType - { - get - { - if ( !string.IsNullOrEmpty( CallResult ) ) - return $"CallResult<{CallResult}>"; - - return $"CallResult"; - } - } -} - -internal class CSteamIdType : BaseType -{ - public override bool IsReturnedWeird => true; } internal class IntType : BaseType @@ -243,54 +167,3 @@ internal class LongType : BaseType { public override string TypeName => $"long"; } - -internal class ConstCharType : BaseType -{ - public override string TypeName => $"string"; - public override string TypeNameFrom => $"Utf8StringPointer"; - public override string AsArgument() => $"[MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] {Ref}{TypeName} {VarName}"; - public override string Ref => ""; -} - -internal class StringType : BaseType -{ - public override string TypeName => $"string"; - public override string AsArgument() => $"out string {VarName}"; - - public override string AsNativeArgument() => $"IntPtr {VarName}"; - - public override string AsCallArgument() => $"mem{VarName}"; - public override string Ref => ""; -} - -internal class VoidType : BaseType -{ - public override string TypeName => $"void"; - public override string TypeNameFrom => $"void"; - public override string Return( string varname ) => $""; - public override bool IsVoid => true; -} - -internal class EnumType : BaseType -{ - -} - -internal class ConstValueType : BaseType -{ - private string Value; - BaseType basetype; - - public ConstValueType( BaseType basetype, string value ) - { - this.basetype = basetype; - this.Value = value; - } - - public override bool ShouldSkipAsArgument => true; - - public override string Ref => ""; - public override bool IsVector => false; - public override string AsArgument() => basetype.AsArgument(); - public override string AsCallArgument() => Value; -} \ No newline at end of file diff --git a/Generator/Types/BoolType.cs b/Generator/Types/BoolType.cs new file mode 100644 index 0000000..9a13141 --- /dev/null +++ b/Generator/Types/BoolType.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + + +/// +/// Special care needs to be taken with bool types. Apparently BOOL in WINDOWS.H are 4 bytes. +/// But in reality and in native c++ they're 1 byte. +/// Of course marshalling by default expects them to be 4 bytes - because why not eh. So we have +/// to add a few attributes to make sure we don't get fucked over by bill gates again. +/// +internal class BoolType : BaseType +{ + public override string TypeName => $"bool"; + public override string AsArgument() => $"[MarshalAs( UnmanagedType.U1 )] {Ref}{TypeName} {VarName}"; + public override string ReturnAttribute => "[return: MarshalAs( UnmanagedType.I1 )]"; + +} \ No newline at end of file diff --git a/Generator/Types/ConstCharType.cs b/Generator/Types/ConstCharType.cs new file mode 100644 index 0000000..493d8cf --- /dev/null +++ b/Generator/Types/ConstCharType.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + + +/// +/// To pass a string we use a custom marshaller (Utf8StringToNative) to convert it +/// from a utf8 string to a pointer. If we just pass it as a string it won't be utf8 encoded. +/// +/// To receive we have a special struct called Utf8StringPointer which can implicitly change +/// the pointer to a utf8 string. +/// +internal class ConstCharType : BaseType +{ + public override string TypeName => $"string"; + public override string TypeNameFrom => $"Utf8StringPointer"; + public override string AsArgument() => $"[MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( Utf8StringToNative ) )] {Ref}{TypeName} {VarName}"; + public override string Ref => ""; +} \ No newline at end of file diff --git a/Generator/Types/FetchStringType.cs b/Generator/Types/FetchStringType.cs new file mode 100644 index 0000000..207ae76 --- /dev/null +++ b/Generator/Types/FetchStringType.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + + +/// +/// Passes a pointer to a buffer as an argument, then converts +/// it into a string which is returned via an out param. +/// +/// This is used of "char *" parameters which expect you to pass in +/// a buffer to retrieve the text. Usually \0 terminated. +/// + +internal class FetchStringType : BaseType +{ + public override string TypeName => $"string"; + public override string AsArgument() => $"out string {VarName}"; + + public override string AsNativeArgument() => $"IntPtr {VarName}"; + + public override string AsCallArgument() => $"mem{VarName}"; + public override string Ref => ""; +} \ No newline at end of file diff --git a/Generator/Types/LiteralType.cs b/Generator/Types/LiteralType.cs new file mode 100644 index 0000000..bec4d2f --- /dev/null +++ b/Generator/Types/LiteralType.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + + +/// +/// Used to replace a variable with a literal. +/// +/// This is used when we can determine a parameter ourselves. For example +/// if you're passing a buffer and a paramter is the buffer length +/// + +internal class LiteralType : BaseType +{ + private string Value; + BaseType basetype; + + public LiteralType( BaseType basetype, string value ) + { + this.basetype = basetype; + this.Value = value; + } + + public override bool ShouldSkipAsArgument => true; + + public override string Ref => ""; + public override bool IsVector => false; + public override string AsArgument() => basetype.AsArgument(); + public override string AsCallArgument() => Value; +} \ No newline at end of file diff --git a/Generator/Types/SteamApiCallType.cs b/Generator/Types/SteamApiCallType.cs new file mode 100644 index 0000000..c9bcf8b --- /dev/null +++ b/Generator/Types/SteamApiCallType.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + + +/// +/// Functions returning SteamAPICall_t are converted to be asyncronhous +/// and return a generic CallResult struct with the T of the result type. +/// +internal class SteamApiCallType : BaseType +{ + public string CallResult; + public override string TypeName => "SteamAPICall_t"; + public override string Return( string varname ) + { + if ( !string.IsNullOrEmpty( CallResult ) ) + return $"return new CallResult<{CallResult}>( {varname} );"; + + return $"return new CallResult( {varname} );"; + } + + public override string ReturnType + { + get + { + if ( !string.IsNullOrEmpty( CallResult ) ) + return $"CallResult<{CallResult}>"; + + return $"CallResult"; + } + } +} \ No newline at end of file diff --git a/Generator/Types/StructType.cs b/Generator/Types/StructType.cs new file mode 100644 index 0000000..6ef7166 --- /dev/null +++ b/Generator/Types/StructType.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + + +/// +/// The struct can be passed as a pointer or as plain old data. +/// If it's a pointer we need to marshal it back to our type +/// (this only happens in a couple of places in the sdk) +/// + +internal class StructType : BaseType +{ + public string StructName; + + public override string TypeName => StructName; + + public override string TypeNameFrom => NativeType.EndsWith( "*" ) ? "IntPtr" : base.ReturnType; + + public override string Return( string varname ) + { + if ( NativeType.EndsWith( "*" ) ) + { + return $"return {varname}.ToType<{TypeName}>();"; + } + + return base.Return( varname ); + } +} \ No newline at end of file diff --git a/Generator/Types/VoidType.cs b/Generator/Types/VoidType.cs new file mode 100644 index 0000000..57d4926 --- /dev/null +++ b/Generator/Types/VoidType.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + + +/// +/// Nothing - just priny void +/// +internal class VoidType : BaseType +{ + public override string TypeName => $"void"; + public override string TypeNameFrom => $"void"; + public override string Return( string varname ) => ""; + public override bool IsVoid => true; +} \ No newline at end of file