From ed96cafbe41a58a88a8dc37c34fd4a24e00dc6bf Mon Sep 17 00:00:00 2001 From: Garry Newman Date: Sat, 22 Feb 2020 20:16:04 +0000 Subject: [PATCH] Updated generator to use flat files --- Generator/Cleanup.cs | 57 ++- Generator/CodeParser/CodeParser.Class.cs | 76 ---- Generator/CodeParser/CodeParser.cs | 174 --------- Generator/CodeParser/ParseClasses.cs | 155 -------- Generator/CodeWriter/ClassVTable.cs | 313 ---------------- Generator/CodeWriter/CodeWriter.cs | 89 +++-- Generator/CodeWriter/Constants.cs | 32 +- Generator/CodeWriter/CustomEnums.cs | 21 ++ Generator/CodeWriter/Enums.cs | 102 ++--- Generator/CodeWriter/GlobalFunctions.cs | 128 ------- Generator/CodeWriter/Interface.cs | 190 ++++++++++ Generator/CodeWriter/Struct.cs | 114 +----- Generator/CodeWriter/StructCallbacks.cs | 75 ++++ Generator/CodeWriter/Types.cs | 16 +- Generator/CodeWriter/Types/BaseType.cs | 268 -------------- Generator/CodeWriter/Utility.cs | 2 + Generator/Generator.csproj | 17 +- Generator/Program.cs | 23 +- Generator/SteamApiDefinition.cs | 126 ++++--- Generator/Types/BaseType.cs | 169 +++++++++ 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 + Generator/steam_api_missing.json | 450 ----------------------- 28 files changed, 959 insertions(+), 1818 deletions(-) delete mode 100644 Generator/CodeParser/CodeParser.Class.cs delete mode 100644 Generator/CodeParser/CodeParser.cs delete mode 100644 Generator/CodeParser/ParseClasses.cs delete mode 100644 Generator/CodeWriter/ClassVTable.cs create mode 100644 Generator/CodeWriter/CustomEnums.cs delete mode 100644 Generator/CodeWriter/GlobalFunctions.cs create mode 100644 Generator/CodeWriter/Interface.cs create mode 100644 Generator/CodeWriter/StructCallbacks.cs delete mode 100644 Generator/CodeWriter/Types/BaseType.cs create mode 100644 Generator/Types/BaseType.cs 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 delete mode 100644 Generator/steam_api_missing.json diff --git a/Generator/Cleanup.cs b/Generator/Cleanup.cs index fd0ccd2..bbd3757 100644 --- a/Generator/Cleanup.cs +++ b/Generator/Cleanup.cs @@ -10,9 +10,15 @@ public static class Cleanup { public static string ConvertType( string type ) { + type = type.Replace( "class ", "" ); + type = type.Replace( "struct ", "" ); + + type = type.Replace( "unsigned long long", "uint64" ); + type = type.Replace( "unsigned int", "uint" ); + type = type.Replace( "uint32", "uint" ); + type = type.Replace( "CSteamID", "SteamId" ); type = type.Replace( "CGameID", "GameId" ); - type = type.Replace( "PersonaState", "FriendState" ); type = type.Replace( "AudioPlayback_Status", "MusicStatus" ); type = type.Replace( "AuthSessionResponse", "AuthResponse" ); type = type.Replace( "FriendRelationship", "Relationship" ); @@ -27,8 +33,9 @@ public static class Cleanup type = type.Replace( "SteamItemDef_t", "InventoryDefId" ); type = type.Replace( "ChatRoomEnterResponse", "RoomEnter" ); type = type.Replace( "SteamNetworkPingLocation_t", "PingLocation" ); + type = type.Replace( "SteamNetworkingConfigValue_t", "NetKeyValue" ); type = type.Replace( "SteamNetworkingConfigValue", "NetConfig" ); - type = type.Replace( "SteamNetworkingConfigScope", "NetScope" ); + type = type.Replace( "SteamNetworkingConfigScope", "NetConfigScope" ); type = type.Replace( "SteamNetworkingConfigDataType", "NetConfigType" ); type = type.Replace( "HSteamNetConnection", "Connection" ); type = type.Replace( "HSteamListenSocket", "Socket" ); @@ -44,6 +51,15 @@ public static class Cleanup type = type.Replace( "InputAnalogActionData_t", "AnalogState" ); type = type.Replace( "InputMotionData_t", "MotionState" ); type = type.Replace( "MatchMakingKeyValuePair_t", "MatchMakingKeyValuePair" ); + type = type.Replace( "ISteamNetworkingMessage", "NetMsg" ); + type = type.Replace( "SteamNetworkingMessage_t", "NetMsg" ); + type = type.Replace( "SteamIPAddress_t", "SteamIPAddress" ); + + type = type.Replace( "::", "." ); + + + if ( type == "EPersonaState" ) return "EFriendState"; + if ( type == "PersonaState" ) return "FriendState"; return type; } @@ -58,6 +74,28 @@ public static class Cleanup if ( type == "DigitalState" ) return false; if ( type == "MotionState" ) return false; if ( type == "MatchMakingKeyValuePair" ) return false; + if ( type == "Connection" ) return false; + if ( type == "Socket" ) return false; + if ( type == "SteamNetworkingMicroseconds" ) return false; + if ( type == "FSteamNetworkingSocketsDebugOutput" ) return false; + if ( type == "NetMsg" ) return false; + if ( type == "SteamDatagramErrMsg" ) return false; + if ( type == "ConnectionInfo" ) return false; + if ( type == "SteamNetworkingIPAddr" ) return false; + if ( type == "NetAddress" ) return false; + if ( type == "NetIdentity" ) return false; + if ( type == "SteamNetworkingQuickConnectionStatus" ) return false; + if ( type == "SteamNetworkingErrMsg" ) return false; + if ( type == "NetKeyValue" ) return false; + if ( type == "SteamIPAddress" ) return false; + if ( type == "PingLocation" ) return false; + if ( type == "CSteamID" ) return false; + if ( type == "CSteamAPIContext" ) return false; + if ( type == "CCallResult" ) return false; + if ( type == "CCallback" ) return false; + if ( type == "ValvePackingSentinel_t" ) return false; + if ( type == "CCallbackBase" ) return false; + if ( type == "CSteamGameServerAPIContext" ) return false; return true; } @@ -88,6 +126,9 @@ public static class Cleanup if ( name == "InputType" ) return "public"; if ( name == "InputSourceMode" ) return "public"; if ( name == "UserHasLicenseForAppResult" ) return "public"; + if ( name == "PingLocation" ) return "public"; + if ( name == "ConnectionState" ) return "public"; + if ( name == "SteamNetworkingAvailability" ) return "public"; return "internal"; } @@ -103,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/CodeParser/CodeParser.Class.cs b/Generator/CodeParser/CodeParser.Class.cs deleted file mode 100644 index bcb72d7..0000000 --- a/Generator/CodeParser/CodeParser.Class.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; - -namespace Generator -{ - partial class CodeParser - { - public class Class - { - public string Name; - public string InterfaceString; - - public class Function - { - public string Name; - public string DuplicateName; - public Dictionary Arguments = new Dictionary(); - - public string ReturnType; - public string CallResult; - } - - public List Functions = new List(); - - internal Function AddFunction( string funcName, string returnType, string args ) - { - if ( funcName == "Init" ) funcName = "DoInit"; - if ( funcName == "Shutdown" ) funcName = "DoShutdown"; - - var f = new Function - { - Name = funcName, - ReturnType = returnType - }; - - args = Regex.Replace( args, "", "" ); - - foreach ( var arg in args.Split( new[] { ',' }, StringSplitOptions.RemoveEmptyEntries ) ) - { - var m = Regex.Match( arg.Trim(), @"(.+?[ |\*|\&])?([a-zA-Z0-9_]+?)( = (.+?))?$" ); - - var t = m.Groups[1].Value.Trim(); - var n = m.Groups[2].Value.Trim(); - - t = Cleanup.ConvertType( t ); - - f.Arguments.Add( n, t ); - } - - Functions.Add( f ); - - return f; - } - - public void PostProcess() - { - var duplicateFunctions = Functions - .GroupBy( x => x.Name ) - .Where( x => x.Count() > 1 ); - - foreach ( var group in duplicateFunctions ) - { - var g = group.ToArray(); - for ( int i=0; i< g.Count(); i++ ) - { - g[i].DuplicateName = g[i].Name; - g[i].Name = $"{g[i].Name}{i+1}"; - } - } - } - } - - } -} diff --git a/Generator/CodeParser/CodeParser.cs b/Generator/CodeParser/CodeParser.cs deleted file mode 100644 index 7c03b15..0000000 --- a/Generator/CodeParser/CodeParser.cs +++ /dev/null @@ -1,174 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; - -namespace Generator -{ - public partial class CodeParser - { - public string Content; - - public CodeParser( string folder ) - { - foreach ( var file in System.IO.Directory.GetFiles( folder, "*.h", System.IO.SearchOption.AllDirectories ) ) - { - Content += System.IO.File.ReadAllText( file ); - } - - Content = Content.Replace( "\r\n", "\n" ); - Content = Content.Replace( "\n\r", "\n" ); - } - - internal void ExtendDefinition( SteamApiDefinition def ) - { - // - // Get a list of CallbackIds - // - def.CallbackIds = new Dictionary(); - - //v1 - { - var r = new Regex( @"enum { (k_[i|I](?:.+)) = ([0-9]+) };" ); - var ma = r.Matches( Content ); - - foreach ( Match m in ma ) - { - def.CallbackIds.Add( m.Groups[1].Value.Substring( 3 ).Replace( "Callbacks", "" ), int.Parse( m.Groups[2].Value ) ); - } - } - - // - // Associate callbackIds with structs - // - foreach ( var t in def.structs ) - { - if ( !string.IsNullOrEmpty( t.CallbackId ) ) continue; - - // Standard style - { - var r = new Regex( @"struct "+t.Name+@"\n{ ?\n(?:.)+enum { k_iCallback = (?:(.+) \+ ([0-9]+)|(.+)) };", RegexOptions.Multiline | RegexOptions.IgnoreCase ); - var m = r.Match( Content ); - if ( m.Success ) - { - var kName = m.Groups[1].Value; - var num = m.Groups[2].Value; - - if ( string.IsNullOrEmpty( kName ) ) - { - kName = m.Groups[3].Value; - num = "0"; - } - - kName = "CallbackIdentifiers." + kName.Substring( 3 ).Replace( "Callbacks", "" ); - - t.CallbackId = $"{kName} + {num}"; - } - } - - // New style - { - var r = new Regex( @"DEFINE_CALLBACK\( "+t.Name+@", (.+) \+ ([0-9]+) \)" ); - var m = r.Match( Content ); - if ( m.Success ) - { - var kName = m.Groups[1].Value; - var num = m.Groups[2].Value; - - //kName = kName.Replace( "k_i", "CallbackIdentifiers." ).Replace( "Callbacks", "" ); - kName = "CallbackIdentifiers." + kName.Substring( 3 ).Replace( "Callbacks", "" ); - - t.CallbackId = $"{kName} + {num}"; - } - } - - // Even Newer Style - { - var r = new Regex( @"STEAM_CALLBACK_BEGIN\( " + t.Name + @", (.+) \+ ([0-9]+) \)" ); - var m = r.Match( Content ); - if ( m.Success ) - { - var kName = m.Groups[1].Value; - var num = m.Groups[2].Value; - - //kName = kName.Replace( "k_i", "CallbackIdentifiers." ).Replace( "Callbacks", "" ); - kName = "CallbackIdentifiers." + kName.Substring( 3 ).Replace( "Callbacks", "" ); - - t.CallbackId = $"{kName} + {num}"; - } - } - } - - // - // Find defines - // - def.Defines = new Dictionary(); - { - var r = new Regex( @"#define ([a-zA-Z_]+) ""(.+)""" ); - var ma = r.Matches( Content ); - - foreach ( Match m in ma ) - { - def.Defines.Add( m.Groups[1].Value.Replace( "Callbacks", "" ), m.Groups[2].Value ); - } - } - - // - // Find CALL_RESULTs - // - { - var r = new Regex( @"CALL_RESULT\( (.+) \)(?:.+)?\n(?:.+)virtual\s+SteamAPICall_t\s+(\w+)\(" ); - var ma = r.Matches( Content ); - - foreach ( Match m in ma ) - { - var s = def.structs.Single( x => x.Name == m.Groups[1].Value ); - s.IsCallResult = true; - - foreach ( var t in def.methods.Where( x => x.Name == m.Groups[2].Value ) ) - { - if ( !string.IsNullOrEmpty( t.CallResult ) ) continue; - - t.CallResult = s.Name; - } - } - } - - // - // Find missing structs - // - { - var r = new Regex( @"struct ([a-zA-Z]+_t)" ); - var ma = r.Matches( Content ); - - foreach ( Match m in ma ) - { - var s = def.structs.SingleOrDefault( x => x.Name == m.Groups[1].Value ); - if ( s == null ) - { - Console.WriteLine( "Missing Struct: " + m.Groups[1].Value ); - } - } - - //Console.ReadKey(); - } - - // - // Change all struct bool fields to bytes (they're technically bytes anyway, and it helps with marshalling) - // - { - foreach ( var s in def.structs ) - { - foreach ( var f in s.Fields ) - { - if ( f.Type == "bool" ) - f.Type = "byte"; - } - } - } - - } - } -} diff --git a/Generator/CodeParser/ParseClasses.cs b/Generator/CodeParser/ParseClasses.cs deleted file mode 100644 index 5ebc6a7..0000000 --- a/Generator/CodeParser/ParseClasses.cs +++ /dev/null @@ -1,155 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; - -namespace Generator -{ - partial class CodeParser - { - public List Classes = new List(); - - public void ParseClasses() - { - var source = RemoveAnnotations( Content ); - - { - var r = new Regex( @"class ([a-zA-Z]+)[\r|\n]+{[\r|\n]((?s).*?)};" ); - var ma = r.Matches( source ); - - foreach ( Match m in ma ) - { - ProcessClass( m.Groups[0].Value.Trim(), m.Groups[1].Value.Trim(), m.Groups[2].Value.Trim() ); - //def.CallbackIds.Add( m.Groups[1].Value.Substring( 3 ).Replace( "Callbacks", "" ), int.Parse( m.Groups[2].Value ) ); - } - } - - Console.WriteLine( "OKay" ); - } - - public void ProcessClass( string fulldef, string classname, string inner ) - { - Console.WriteLine( $"Class: {classname} " ); - - var lines = inner.Split( new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries ); - - var func = new Regex( @"virtual (.+[\t |\*])([a-z0-9A-Z]+?)\((.+?)?\) = 0 ?;$" ); - - var c = new Class(); - c.Name = classname; - - var interfaceMatch = Regex.Match( Content, $"#define {classname.ToUpper().Substring( 1 )}_INTERFACE_VERSION \"(.+?)\"" ); - if ( interfaceMatch.Success ) - { - c.InterfaceString = interfaceMatch.Groups[1].Value; - } - - var lastCallResult = ""; - - var partialLine = ""; - - var needsEndIf = false; - - foreach ( var linestr in lines ) - { - var line = linestr.Trim(); - - var commentPos = line.IndexOf( "//" ); - if ( commentPos > 0 ) - line = line.Substring( 0, commentPos-1 ).Trim(); - - if ( line.Trim().Length < 4 ) continue; - if ( line.Trim().StartsWith( "public:" ) ) continue; - if ( line.Trim().StartsWith( "//" ) ) continue; - - if ( line.Trim().StartsWith( "#ifdef _PS3" ) || line.Trim().StartsWith( "#if defined(_PS3)" ) ) - { - needsEndIf = true; - continue; - } - - if ( needsEndIf ) - { - needsEndIf = !line.Trim().StartsWith( "#endif" ); - continue; - } - - var callresult = Regex.Match( line, @"STEAM_CALL_RESULT\((.+?)\)" ); - if ( callresult.Success ) - { - partialLine = ""; - lastCallResult = callresult.Groups[1].Value.Trim(); - continue; - } - - if ( !string.IsNullOrEmpty( partialLine ) ) - { - partialLine += " " + line.Trim(); - - if ( !partialLine.Trim().EndsWith( ";" ) ) - continue; - - line = partialLine; - partialLine = ""; - } - - var f = func.Match( line ); - if ( f.Success ) - { - var returnType = f.Groups[1].Value.Trim(); - var funcName = f.Groups[2].Value.Trim(); - var args = f.Groups[3].Value.Trim(); - // Console.WriteLine( $"Function: {funcName} returns {returnType} with args {args}" ); - - if ( funcName == "RequestUGCDetails" ) lastCallResult = "SteamUGCRequestUGCDetailsResult_t"; - if ( funcName == "DownloadClanActivityCounts" ) lastCallResult = "DownloadClanActivityCountsResult_t"; - - if ( funcName.Contains( ' ' ) || funcName.Contains( '*' ) ) - throw new System.Exception( "Parsing Error!" ); - - var fnc = c.AddFunction( funcName, returnType, args ); - - fnc.CallResult = lastCallResult; - lastCallResult = null; - partialLine = ""; - } - else - { - if ( line.Trim().StartsWith( "virtual " ) ) - { - partialLine = line; - } - - Console.WriteLine( $"Unknown Line: {line}" ); - } - } - - c.PostProcess(); - - Classes.Add( c ); - } - - public string RemoveAnnotations( string str ) - { - str = Regex.Replace( str, @"STEAM_OUT_ARRAY_CALL\((.+?)\)", "" ); - str = Regex.Replace( str, @"STEAM_PRIVATE_API\((.+)\)", "$1" ); - str = Regex.Replace( str, @"STEAM_ARRAY_COUNT\((.+?)\) ", "" ); - str = Regex.Replace( str, @"STEAM_OUT_STRUCT\(\) ", "" ); - str = Regex.Replace( str, @"STEAM_OUT_STRUCT\((.+?)\) ", "" ); - str = Regex.Replace( str, @"STEAM_OUT_ARRAY_COUNT\((.+?)\)", "" ); - str = Regex.Replace( str, @"STEAM_ARRAY_COUNT_D\((.+?)\)", "" ); - str = Regex.Replace( str, @"STEAM_OUT_STRING_COUNT\((.+?)\)", "" ); - str = Regex.Replace( str, @"STEAM_OUT_STRING\(\) ", "" ); - str = Regex.Replace( str, @"STEAM_OUT_BUFFER_COUNT\((.+?)\) ", "" ); - str = Regex.Replace( str, @"STEAM_BUFFER_COUNT\((.+?)\) ", "" ); - str = Regex.Replace( str, @"STEAM_DESC\((.+?)\) ", "" ); - - - - return str; - } - - } -} diff --git a/Generator/CodeWriter/ClassVTable.cs b/Generator/CodeWriter/ClassVTable.cs deleted file mode 100644 index 84b444b..0000000 --- a/Generator/CodeWriter/ClassVTable.cs +++ /dev/null @@ -1,313 +0,0 @@ -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;" ); - WriteLine( $"using Steamworks.Data;" ); - WriteLine(); - - WriteLine(); - - StartBlock( $"namespace Steamworks" ); - { - StartBlock( $"internal class {clss.Name} : SteamInterface" ); - { - WriteLine( $"public override string InterfaceName => \"{clss.InterfaceString}\";" ); - WriteLine(); - - WriteFunctionPointerReader( clss ); - - WriteLine(); - - foreach ( var func in clss.Functions ) - { - 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 ) - { - // TODO - we'll probably have to do this PER platform - - int[] standardLocations = new int[clss.Functions.Count]; - int[] windowsLocations = new int[clss.Functions.Count]; - - for ( int i = 0; i < clss.Functions.Count; i++ ) - { - windowsLocations[i] = i * 8; - standardLocations[i] = i * 8; - } - - // - // 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 ); - } - - if ( clss.Name == "ISteamGameServerStats" ) - { - Swap( clss, "GetUserStat1", "GetUserStat2", windowsLocations ); - Swap( clss, "SetUserStat1", "SetUserStat2", windowsLocations ); - } - - if ( clss.Name == "ISteamUGC" ) - { - Swap( clss, "CreateQueryAllUGCRequest1", "CreateQueryAllUGCRequest2", windowsLocations ); - } - - - StartBlock( $"public override void InitInternals()" ); - { - var different = new List(); - - for ( int i = 0; i < clss.Functions.Count; i++ ) - { - var func = clss.Functions[i]; - - if ( Cleanup.IsDeprecated( $"{clss.Name}.{func.Name}" ) ) - { - WriteLine( $" // {func.Name} is deprecated" ); - } - else - { - if ( standardLocations[i] != windowsLocations[i] ) - { - different.Add( i ); - continue; - } - - WriteLine( $"_{func.Name} = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, Platform.MemoryOffset( {standardLocations[i]} ) ) );" ); - } - } - - if ( different.Count > 0 ) - { - WriteLine( "" ); - WriteLine( "#if PLATFORM_WIN" ); - foreach ( var i in different ) - { - var func = clss.Functions[i]; - WriteLine( $"_{func.Name} = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, Platform.MemoryOffset( {windowsLocations[i]} ) ) );" ); - } - WriteLine( "#else" ); - foreach ( var i in different ) - { - var func = clss.Functions[i]; - WriteLine( $"_{func.Name} = Marshal.GetDelegateForFunctionPointer( Marshal.ReadIntPtr( VTable, Platform.MemoryOffset( {standardLocations[i]} ) ) );" ); - } - 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; - } - - - 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 ); - returnType.Func = func.Name; - - 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 !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; - - 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" ); - - 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 ) - { - 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(); - } - } -} diff --git a/Generator/CodeWriter/CodeWriter.cs b/Generator/CodeWriter/CodeWriter.cs index f1c8c23..c4d79b3 100644 --- a/Generator/CodeWriter/CodeWriter.cs +++ b/Generator/CodeWriter/CodeWriter.cs @@ -10,14 +10,14 @@ namespace Generator { public partial class CodeWriter { + public static CodeWriter Current { get; private set; } + private SteamApiDefinition def; - public CodeParser Parser; - public CodeWriter( CodeParser parser, SteamApiDefinition def ) + public CodeWriter( SteamApiDefinition def ) { - Parser = parser; - this.def = def; + Current = this; WorkoutTypes(); } @@ -29,6 +29,14 @@ namespace Generator Enums(); Footer(); System.IO.File.WriteAllText( $"{folder}SteamEnums.cs", sb.ToString() ); + } + + { + sb = new StringBuilder(); + Header(); + CustomEnums(); + Footer(); + System.IO.File.WriteAllText( $"{folder}CustomEnums.cs", sb.ToString() ); } { @@ -39,6 +47,14 @@ namespace Generator System.IO.File.WriteAllText( $"{folder}SteamTypes.cs", sb.ToString() ); } + { + sb = new StringBuilder(); + Header( "Steamworks.Data" ); + StructCallbacks(); + Footer(); + System.IO.File.WriteAllText( $"{folder}SteamCallbacks.cs", sb.ToString() ); + } + { sb = new StringBuilder(); Header( "Steamworks.Data" ); @@ -56,34 +72,15 @@ namespace Generator } { - GenerateGlobalFunctions( "SteamAPI", $"{folder}../Generated/SteamAPI.cs" ); - GenerateGlobalFunctions( "SteamGameServer", $"{folder}../Generated/SteamGameServer.cs" ); - GenerateGlobalFunctions( "SteamInternal", $"{folder}../Generated/SteamInternal.cs" ); + // GenerateGlobalFunctions( "SteamAPI", $"{folder}../Generated/SteamAPI.cs" ); + // GenerateGlobalFunctions( "SteamGameServer", $"{folder}../Generated/SteamGameServer.cs" ); + // GenerateGlobalFunctions( "SteamInternal", $"{folder}../Generated/SteamInternal.cs" ); } - { - GenerateVTableClass( "ISteamApps", $"{folder}../Generated/Interfaces/ISteamApps.cs" ); - GenerateVTableClass( "ISteamUtils", $"{folder}../Generated/Interfaces/ISteamUtils.cs" ); - GenerateVTableClass( "ISteamParentalSettings", $"{folder}../Generated/Interfaces/ISteamParentalSettings.cs" ); - GenerateVTableClass( "ISteamMusic", $"{folder}../Generated/Interfaces/ISteamMusic.cs" ); - GenerateVTableClass( "ISteamVideo", $"{folder}../Generated/Interfaces/ISteamVideo.cs" ); - GenerateVTableClass( "ISteamUser", $"{folder}../Generated/Interfaces/ISteamUser.cs" ); - GenerateVTableClass( "ISteamMatchmakingServers", $"{folder}../Generated/Interfaces/ISteamMatchmakingServers.cs" ); - GenerateVTableClass( "ISteamFriends", $"{folder}../Generated/Interfaces/ISteamFriends.cs" ); - GenerateVTableClass( "ISteamGameServer", $"{folder}../Generated/Interfaces/ISteamGameServer.cs" ); - GenerateVTableClass( "ISteamScreenshots", $"{folder}../Generated/Interfaces/ISteamScreenshots.cs" ); - GenerateVTableClass( "ISteamUserStats", $"{folder}../Generated/Interfaces/ISteamUserStats.cs" ); - GenerateVTableClass( "ISteamUGC", $"{folder}../Generated/Interfaces/ISteamUGC.cs" ); - GenerateVTableClass( "ISteamRemoteStorage", $"{folder}../Generated/Interfaces/ISteamRemoteStorage.cs" ); - GenerateVTableClass( "ISteamInventory", $"{folder}../Generated/Interfaces/ISteamInventory.cs" ); - GenerateVTableClass( "ISteamNetworking", $"{folder}../Generated/Interfaces/ISteamNetworking.cs" ); - GenerateVTableClass( "ISteamMatchmaking", $"{folder}../Generated/Interfaces/ISteamMatchmaking.cs" ); - GenerateVTableClass( "ISteamParties", $"{folder}../Generated/Interfaces/ISteamParties.cs" ); - GenerateVTableClass( "ISteamNetworkingUtils", $"{folder}../Generated/Interfaces/ISteamNetworkingUtils.cs" ); - GenerateVTableClass( "ISteamNetworkingSockets", $"{folder}../Generated/Interfaces/ISteamNetworkingSockets.cs" ); - GenerateVTableClass( "ISteamGameServerStats", $"{folder}../Generated/Interfaces/ISteamGameServerStats.cs" ); - GenerateVTableClass( "ISteamInput", $"{folder}../Generated/Interfaces/ISteamInput.cs" ); - } + foreach ( var iface in def.Interfaces ) + { + GenerateInterface( iface, $"{folder}../Generated/Interfaces/" ); + } } void WorkoutTypes() @@ -125,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/Constants.cs b/Generator/CodeWriter/Constants.cs index 3f31841..3aa6126 100644 --- a/Generator/CodeWriter/Constants.cs +++ b/Generator/CodeWriter/Constants.cs @@ -10,17 +10,31 @@ namespace Generator { private void Constants() { - StartBlock( "internal static class CallbackIdentifiers" ); - foreach ( var o in def.CallbackIds ) - { - WriteLine( $"public const int {o.Key} = {o.Value};" ); - } - EndBlock(); - StartBlock( "internal static class Defines" ); - foreach ( var o in def.Defines ) + foreach ( var o in def.Consts ) { - WriteLine( $"internal const string {o.Key} = \"{o.Value}\";" ); + var type = o.Type; + type = Cleanup.ConvertType( type ); + + var val = o.Val; + + // Don't need to ull in c# + if ( val.EndsWith( "ull" ) ) + val = val.Replace( "ull", "" ); + + val = val.Replace( "uint32", "uint" ); + val = val.Replace( "16U", "16" ); + val = val.Replace( "8U", "8" ); + + // we're not an actual typedef so can't cast like this + val = val.Replace( "( SteamItemInstanceID_t ) ~ 0", "~default(ulong)" ); + + // This is defined as 0xffffffff - which is too big for an int + // It seems like the loop around is required, so we just hard code it + if ( o.Name == "HSERVERQUERY_INVALID" && val == "0xffffffff" ) + val = "-1"; + + WriteLine( $"internal static readonly {type} {o.Name} = {val};" ); } EndBlock(); } diff --git a/Generator/CodeWriter/CustomEnums.cs b/Generator/CodeWriter/CustomEnums.cs new file mode 100644 index 0000000..5abf746 --- /dev/null +++ b/Generator/CodeWriter/CustomEnums.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Generator +{ + public partial class CodeWriter + { + void CustomEnums() + { + StartBlock( "enum CallbackType" ); + foreach ( var c in def.callback_structs.OrderBy( x => x.CallbackId ) ) + { + WriteLine( $"{c.Name.Replace( "_t", "" ) } = {c.CallbackId}," ); + } + EndBlock(); + } + } +} diff --git a/Generator/CodeWriter/Enums.cs b/Generator/CodeWriter/Enums.cs index cc5c6be..ac78b6a 100644 --- a/Generator/CodeWriter/Enums.cs +++ b/Generator/CodeWriter/Enums.cs @@ -25,55 +25,75 @@ namespace Generator if ( name[0] == 'E' ) name = name.Substring( 1 ); - name = Cleanup.ConvertType( name ); + name = Cleanup.ConvertType( name ); - if ( !Cleanup.ShouldCreate( name ) ) - continue; + if ( !Cleanup.ShouldCreate( name ) ) + continue; - StartBlock( $"{Cleanup.Expose( name )} enum {name} : int" ); + var lowest = o.Values.Min( x => long.Parse( x.Value ) ); + var highest = o.Values.Max( x => long.Parse( x.Value ) ); + + var t = "int"; + + if ( highest > int.MaxValue ) + t = "uint"; + + WriteEnum( o, name, t ); + } + } + + private void WriteEnum( SteamApiDefinition.EnumDef o, string name, string t = "int" ) + { + StartBlock( $"{Cleanup.Expose( name )} enum {name} : {t}" ); + { + // + // If all the enum values start with the same + // string, remove it. This converts + // "k_EUserHasLicenseResultHasLicense" to "HasLicense" etc + // + int iFinished = int.MaxValue; + for ( int i = 0; i < 4096; i++ ) { - // - // If all the enum values start with the same - // string, remove it. This converts - // "k_EUserHasLicenseResultHasLicense" to "HasLicense" etc - // - int iFinished = int.MaxValue; - for ( int i = 0; i < 4096; i++ ) - { - var c = o.Values.First().Name[i]; - foreach ( var entry in o.Values ) - { - if ( entry.Name[i] != c ) - { - iFinished = i; - break; - } - } - - if ( iFinished != int.MaxValue ) - break; - } - + var c = o.Values.First().Name[i]; foreach ( var entry in o.Values ) { - var ename = entry.Name; - - if ( iFinished != int.MaxValue ) - ename = ename.Substring( iFinished ); - - // - // Names aren't allowed to start with a number - // So just stick the enum name on the front - // - if ( char.IsNumber( ename[0] ) ) - ename = name + ename; - - WriteLine( $"{ename} = {entry.Value}," ); + if ( entry.Name[i] != c ) + { + iFinished = i; + break; + } } + + if ( iFinished != int.MaxValue ) + break; + } + + foreach ( var entry in o.Values ) + { + var ename = entry.Name; + + if ( iFinished != int.MaxValue ) + ename = ename.Substring( iFinished ); + + // + // Names aren't allowed to start with a number + // So just stick the enum name on the front + // + if ( char.IsNumber( ename[0] ) ) + { + var p = name; + + if ( p == "HTTPStatusCode" ) p = "Code"; + if ( p == "SteamIPType" ) p = "Type"; + + ename = p + ename; + } + + WriteLine( $"{ename.Trim( ' ', '_' )} = {entry.Value}," ); } - EndBlock(); - WriteLine(); } + EndBlock(); + WriteLine(); } } } diff --git a/Generator/CodeWriter/GlobalFunctions.cs b/Generator/CodeWriter/GlobalFunctions.cs deleted file mode 100644 index 3f4224f..0000000 --- a/Generator/CodeWriter/GlobalFunctions.cs +++ /dev/null @@ -1,128 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Generator -{ - public partial class CodeWriter - { - public void GenerateGlobalFunctions( string startingWith, string filename ) - { - var functions = def.methods.Where( x => x.Name.StartsWith( startingWith ) ); - - sb = new StringBuilder(); - - WriteLine( $"using System;" ); - WriteLine( $"using System.Runtime.InteropServices;" ); - WriteLine( $"using System.Text;" ); - WriteLine( $"using System.Threading.Tasks;" ); - WriteLine( $"using Steamworks.Data;" ); - WriteLine(); - - WriteLine(); - - StartBlock( $"namespace Steamworks" ); - { - StartBlock( $"internal static class {startingWith}" ); - { - StartBlock( $"internal static class Native" ); - { - foreach ( var func in functions ) - { - WriteMarshalledFunction( func ); - } - } - EndBlock(); - - foreach ( var func in functions ) - { - WriteGlobalFunction( startingWith, func ); - WriteLine(); - } - - } - EndBlock(); - } - EndBlock(); - - System.IO.File.WriteAllText( $"{filename}", sb.ToString().Replace( "( )", "()" ) ); - } - - private void WriteGlobalFunction( string cname, SteamApiDefinition.MethodDef func ) - { - var cleanName = func.Name.Substring( cname.Length ).Trim( '_' ); - - var returnType = BaseType.Parse( func.ReturnType ); - returnType.Func = func.Name; - - if ( func.Params == null ) - func.Params = new SteamApiDefinition.MethodDef.ParamType[0]; - - var args = func.Params.Select( x => - { - var bt = BaseType.Parse( x.Type, x.Name ); - bt.Func = func.Name; - return bt; - } ).ToArray(); - var argstr = string.Join( ", ", args.Select( x => x.AsArgument() ) ); - var delegateargstr = string.Join( ", ", args.Select( x => x.AsArgument() ) ); - - if ( returnType.IsReturnedWeird ) - { - throw new System.Exception( "TODO" ); - } - - StartBlock( $"static internal {returnType.ReturnType} {cleanName}( {argstr} )" ); - { - var callargs = string.Join( ", ", args.Select( x => x.AsCallArgument() ) ); - - if ( returnType.IsReturnedWeird ) - { - WriteLine( $"var retVal = default( {returnType.TypeName} );" ); - WriteLine( $"Native.{func.Name}( ref retVal, {callargs} );" ); - WriteLine( $"{returnType.Return( "retVal" )}" ); - } - else if ( returnType.IsVoid ) - { - WriteLine( $"Native.{func.Name}( {callargs} );" ); - } - else - { - var v = $"Native.{func.Name}( {callargs} )"; - - WriteLine( returnType.Return( v ) ); - } - } - EndBlock(); - - } - - private void WriteMarshalledFunction( SteamApiDefinition.MethodDef func ) - { - var returnType = BaseType.Parse( func.ReturnType ); - returnType.Func = func.Name; - - if ( func.Params == null ) - func.Params = new SteamApiDefinition.MethodDef.ParamType[0]; - - var args = func.Params.Select( x => - { - var bt = BaseType.Parse( x.Type, x.Name ); - bt.Func = func.Name; - return bt; - } ).ToArray(); - var argstr = string.Join( ", ", args.Select( x => x.AsArgument() ) ); - var delegateargstr = string.Join( ", ", args.Select( x => x.AsArgument() ) ); - - WriteLine( $"[DllImport( Platform.LibraryName, EntryPoint = \"{func.Name}\", CallingConvention = CallingConvention.Cdecl )]" ); - - if ( returnType.ReturnAttribute != null ) - WriteLine( returnType.ReturnAttribute ); - - WriteLine( $"public static extern {(returnType.IsReturnedWeird ? "void" : returnType.TypeNameFrom)} {func.Name}( {delegateargstr} );" ); - WriteLine(); - } - } -} diff --git a/Generator/CodeWriter/Interface.cs b/Generator/CodeWriter/Interface.cs new file mode 100644 index 0000000..c35d22f --- /dev/null +++ b/Generator/CodeWriter/Interface.cs @@ -0,0 +1,190 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Generator +{ + public partial class CodeWriter + { + public void GenerateInterface( SteamApiDefinition.Interface iface, string folder ) + { + sb = new StringBuilder(); + + WriteLine( $"using System;" ); + WriteLine( $"using System.Runtime.InteropServices;" ); + WriteLine( $"using System.Text;" ); + WriteLine( $"using System.Threading.Tasks;" ); + WriteLine( $"using Steamworks.Data;" ); + WriteLine(); + + WriteLine(); + + StartBlock( $"namespace Steamworks" ); + { + StartBlock( $"internal class {iface.Name} : SteamInterface" ); + { + WriteLine(); + StartBlock( $"internal {iface.Name}( bool IsGameServer )" ); + { + WriteLine( $"SetupInterface( IsGameServer );" ); + } + EndBlock(); + WriteLine(); + + if ( iface.Accessors != null ) + { + foreach ( var func in iface.Accessors ) + { + WriteLine( $"[DllImport( Platform.LibraryName, EntryPoint = \"{func.Name_Flat}\", CallingConvention = Platform.CC)]" ); + WriteLine( $"internal static extern IntPtr {func.Name_Flat}();" ); + + if ( func.Kind == "user" ) + { + WriteLine( $"public override IntPtr GetUserInterfacePointer() => {func.Name_Flat}();" ); + } + else if ( func.Kind == "gameserver" ) + { + WriteLine( $"public override IntPtr GetServerInterfacePointer() => {func.Name_Flat}();" ); + } + else if ( func.Kind == "global" ) + { + WriteLine( $"public override IntPtr GetGlobalInterfacePointer() => {func.Name_Flat}();" ); + } + else + { + throw new Exception( $"unknown Kind {func.Kind}" ); + } + } + + WriteLine(); + WriteLine(); + } + + foreach ( var func in iface.Methods ) + { + if ( Cleanup.IsDeprecated( $"{iface.Name}.{func.Name}" ) ) + continue; + + WriteFunction( iface, func ); + WriteLine(); + } + + } + EndBlock(); + } + EndBlock(); + + System.IO.File.WriteAllText( $"{folder}{iface.Name}.cs", sb.ToString() ); + } + + private void WriteFunction( SteamApiDefinition.Interface iface, SteamApiDefinition.Interface.Method func ) + { + var returnType = BaseType.Parse( func.ReturnType, null, func.CallResult ); + returnType.Func = func.Name; + + if ( func.Params == null ) + func.Params = new SteamApiDefinition.Interface.Method.Param[0]; + + var args = func.Params.Select( x => + { + var bt = BaseType.Parse( x.ParamType, x.ParamName ); + bt.Func = func.Name; + return bt; + } ).ToArray(); + + for( int i=0; i !x.ShouldSkipAsArgument ).Select( x => x.AsArgument() ) ); ; + var delegateargstr = string.Join( ", ", args.Select( x => x.AsNativeArgument() ) ); + + if ( returnType is SteamApiCallType sap ) + { + sap.CallResult = func.CallResult; + argstr = string.Join( ", ", args.Select( x => x.AsArgument().Replace( "ref ", " /* ref */ " ) ) ); + } + + WriteLine( $"#region FunctionMeta" ); + + WriteLine( $"[DllImport( Platform.LibraryName, EntryPoint = \"{func.FlatName}\", CallingConvention = Platform.CC)]" ); + + if ( returnType.ReturnAttribute != null ) + WriteLine( returnType.ReturnAttribute ); + + WriteLine( $"private static extern {returnType.TypeNameFrom} _{func.Name}( IntPtr self, {delegateargstr} );".Replace( "( IntPtr self, )", "( IntPtr self )" ) ); + + WriteLine(); + WriteLine( $"#endregion" ); + + + if ( !string.IsNullOrEmpty( func.Desc ) ) + { + WriteLine( "/// " ); + WriteLine( $"/// {func.Desc}" ); + WriteLine( "/// " ); + } + + 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 FetchStringType sb ) + { + WriteLine( $"IntPtr mem{sb.VarName} = Helpers.TakeMemory();" ); + } + } + + if ( returnType.IsVoid ) + { + 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 FetchStringType sb ) + { + WriteLine( $"{sb.VarName} = Helpers.MemoryToString( mem{sb.VarName} );" ); + } + } + + // + // Return + // + if ( !returnType.IsVoid ) + { + WriteLine( returnType.Return( "returnValue" ) ); + } + } + EndBlock(); + } + } +} diff --git a/Generator/CodeWriter/Struct.cs b/Generator/CodeWriter/Struct.cs index 69059f4..04ead4b 100644 --- a/Generator/CodeWriter/Struct.cs +++ b/Generator/CodeWriter/Struct.cs @@ -17,20 +17,6 @@ namespace Generator private Dictionary TypeDefs = new Dictionary(); - // - // Don't give a fuck about these classes, they just cause us trouble - // - public readonly static string[] SkipStructs = new string[] - { - "CSteamID", - "CSteamAPIContext", - "CCallResult", - "CCallback", - "ValvePackingSentinel_t", - "CCallbackBase", - "CSteamGameServerAPIContext" - }; - public readonly static string[] ForceLargePackStructs = new string[] { "LeaderboardEntry_t" @@ -38,15 +24,10 @@ namespace Generator void Structs() { - var callbackList = new List(); - foreach ( var c in def.structs ) { var name = Cleanup.ConvertType( c.Name ); - if ( SkipStructs.Contains( c.Name ) ) - continue; - if ( !Cleanup.ShouldCreate( name ) ) continue; @@ -55,12 +36,10 @@ namespace Generator int defaultPack = c.IsPack4OnWindows ? 4 : 8; - var isCallback = !string.IsNullOrEmpty( c.CallbackId ); - - // - // Main struct - // - WriteLine( $"[StructLayout( LayoutKind.Sequential, Pack = Platform.{(c.IsPack4OnWindows?"StructPackSize": "StructPlatformPackSize")} )]" ); + // + // Main struct + // + WriteLine( $"[StructLayout( LayoutKind.Sequential, Pack = Platform.{(c.IsPack4OnWindows?"StructPackSize": "StructPlatformPackSize")} )]" ); StartBlock( $"{Cleanup.Expose( name )} struct {name}" ); { // @@ -69,80 +48,12 @@ namespace Generator StructFields( c.Fields ); WriteLine(); - if ( isCallback ) + if ( c.Enums != null ) { - WriteLine( "#region SteamCallback" ); - { - - WriteLine( $"internal static readonly int StructSize = System.Runtime.InteropServices.Marshal.SizeOf( typeof({name}) );" ); - WriteLine( $"internal static {name} Fill( IntPtr p ) => (({name})({name}) Marshal.PtrToStructure( p, typeof({name}) ) );" ); - WriteLine(); - WriteLine( $"static Action<{name}> actionClient;" ); - WriteLine( $"[MonoPInvokeCallback] static void OnClient( IntPtr thisptr, IntPtr pvParam ) => actionClient?.Invoke( Fill( pvParam ) );" ); - - WriteLine( $"static Action<{name}> actionServer;" ); - WriteLine( $"[MonoPInvokeCallback] static void OnServer( IntPtr thisptr, IntPtr pvParam ) => actionServer?.Invoke( Fill( pvParam ) );" ); - - StartBlock( $"public static void Install( Action<{name}> action, bool server = false )" ); - { - StartBlock( "if ( server )" ); - { - WriteLine( $"Event.Register( OnServer, StructSize, {c.CallbackId}, true );" ); - WriteLine( $"actionServer = action;" ); - } - Else(); - { - WriteLine( $"Event.Register( OnClient, StructSize, {c.CallbackId}, false );" ); - WriteLine( $"actionClient = action;" ); - } - EndBlock(); - - } - EndBlock(); - - StartBlock( $"public static async Task<{name}?> GetResultAsync( SteamAPICall_t handle )" ); - { - WriteLine( $"bool failed = false;" ); - WriteLine(); - StartBlock( $"while ( !SteamUtils.IsCallComplete( handle, out failed ) )" ); - { - WriteLine( $"await Task.Delay( 1 );" ); - WriteLine( $"if ( !SteamClient.IsValid && !SteamServer.IsValid ) return null;" ); - } - EndBlock(); - - WriteLine( $"if ( failed ) return null;" ); - WriteLine( $"" ); - WriteLine( $"var ptr = Marshal.AllocHGlobal( StructSize );" ); - WriteLine( $"" ); - WriteLine( $"try" ); - WriteLine( $"{{" ); - WriteLine( $" if ( !SteamUtils.Internal.GetAPICallResult( handle, ptr, StructSize, {c.CallbackId}, ref failed ) || failed )" ); - WriteLine( $" return null;" ); - WriteLine( $"" ); - WriteLine( $" return Fill( ptr );" ); - WriteLine( $"}}" ); - WriteLine( $"finally" ); - WriteLine( $"{{" ); - WriteLine( $" Marshal.FreeHGlobal( ptr );" ); - WriteLine( $"}}" ); - } - EndBlock(); - } - WriteLine( "#endregion" ); - } - else - { - WriteLine( "#region Marshalling" ); - { - WriteLine( $"internal static {name} Fill( IntPtr p ) => (({name})({name}) Marshal.PtrToStructure( p, typeof({name}) ) );" ); - } - WriteLine( "#endregion" ); - } - - if ( !string.IsNullOrEmpty( c.CallbackId ) ) - { - callbackList.Add( c ); + foreach ( var e in c.Enums ) + { + WriteEnum( e, e.Name ); + } } } @@ -206,6 +117,13 @@ namespace Generator WriteLine( $"[MarshalAs(UnmanagedType.ByValArray, SizeConst = {num}, ArraySubType = UnmanagedType.U4)]" ); } + if ( t.StartsWith( "uint " ) && t.Contains( "[" ) ) + { + var num = t.Replace( "uint", "" ).Trim( '[', ']', ' ' ); + t = $"uint[]"; + WriteLine( $"[MarshalAs(UnmanagedType.ByValArray, SizeConst = {num}, ArraySubType = UnmanagedType.U4)]" ); + } + if ( t.StartsWith( "float " ) && t.Contains( "[" ) ) { var num = t.Replace( "float", "" ).Trim( '[', ']', ' ' ); diff --git a/Generator/CodeWriter/StructCallbacks.cs b/Generator/CodeWriter/StructCallbacks.cs new file mode 100644 index 0000000..ab81bb7 --- /dev/null +++ b/Generator/CodeWriter/StructCallbacks.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Generator +{ + public partial class CodeWriter + { + void StructCallbacks() + { + var callbackList = new List(); + + foreach ( var c in def.callback_structs ) + { + var name = Cleanup.ConvertType( c.Name ); + + if ( !Cleanup.ShouldCreate( name ) ) + continue; + + if ( name.Contains( "::" ) ) + continue; + + int defaultPack = c.IsPack4OnWindows ? 4 : 8; + + var isCallback = true; + var iface = ""; + if ( isCallback ) + iface = " : ICallbackData"; + + // + // Main struct + // + WriteLine( $"[StructLayout( LayoutKind.Sequential, Pack = Platform.{(c.IsPack4OnWindows?"StructPackSize": "StructPlatformPackSize")} )]" ); + StartBlock( $"{Cleanup.Expose( name )} struct {name}{iface}" ); + { + // + // The fields + // + StructFields( c.Fields ); + WriteLine(); + + if ( isCallback ) + { + WriteLine( "#region SteamCallback" ); + { + + WriteLine( $"public static int _datasize = System.Runtime.InteropServices.Marshal.SizeOf( typeof({name}) );" ); + WriteLine( $"public int DataSize => _datasize;" ); + WriteLine( $"public CallbackType CallbackType => CallbackType.{name.Replace( "_t", "" )};" ); + } + WriteLine( "#endregion" ); + } + + if ( c.Enums != null ) + { + foreach ( var e in c.Enums ) + { + WriteEnum( e, e.Name ); + } + } + + // if ( c.CallbackId ) ) + { + callbackList.Add( c ); + } + + } + EndBlock(); + WriteLine(); + } + } + } +} diff --git a/Generator/CodeWriter/Types.cs b/Generator/CodeWriter/Types.cs index a4f2b1b..9b8123f 100644 --- a/Generator/CodeWriter/Types.cs +++ b/Generator/CodeWriter/Types.cs @@ -37,7 +37,10 @@ namespace Generator { foreach ( var o in def.typedefs.Where( x => !x.Name.Contains( "::" ) ) ) { - var typeName = Cleanup.ConvertType( o.Name ); + if ( !Cleanup.ShouldCreate( o.Name ) ) + continue; + + var typeName = Cleanup.ConvertType( o.Name ); if ( !Cleanup.ShouldCreate( typeName ) ) continue; @@ -50,7 +53,16 @@ namespace Generator StartBlock( $"{Cleanup.Expose( typeName )} struct {typeName} : IEquatable<{typeName}>, IComparable<{typeName}>" ); { - WriteLine( $"public {ToManagedType( o.Type )} Value;" ); + WriteLine( $"// Name: {o.Name}, Type: {o.Type}" ); + + if ( o.Type == "char [1024]" ) + { + WriteLine( $"public fixed char[1024] Value;" ); + } + else + { + WriteLine( $"public {ToManagedType( o.Type )} Value;" ); + } WriteLine(); WriteLine( $"public static implicit operator {typeName}( {ToManagedType( o.Type )} value ) => new {typeName}(){{ Value = value }};" ); WriteLine( $"public static implicit operator {ToManagedType( o.Type )}( {typeName} value ) => value.Value;" ); diff --git a/Generator/CodeWriter/Types/BaseType.cs b/Generator/CodeWriter/Types/BaseType.cs deleted file mode 100644 index 1e03c50..0000000 --- a/Generator/CodeWriter/Types/BaseType.cs +++ /dev/null @@ -1,268 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -internal class BaseType -{ - public string VarName; - public string NativeType; - public virtual string TypeName => $"{NativeType}"; - public virtual string TypeNameFrom => TypeName; - - public string Func; - - public virtual bool WindowsSpecific => false; - - public static BaseType Parse( string type, string varname = null ) - { - type = Cleanup.ConvertType( type ); - - if ( varname == "ppOutMessages" ) return new PointerType { NativeType = "void *", VarName = varname }; - if ( type == "SteamAPIWarningMessageHook_t" ) return new PointerType { NativeType = type, VarName = varname }; - - if ( type == "SteamAPICall_t" ) return new SteamApiCallType { NativeType = type, VarName = varname }; - - 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 }; - - var basicType = type.Replace( "const ", "" ).Trim( ' ', '*', '&' ); - - if ( basicType == "void" ) return new PointerType { NativeType = type, VarName = varname }; - if ( basicType.StartsWith( "ISteam" ) ) return new PointerType { NativeType = type, VarName = varname }; - if ( basicType == "const void" ) return new PointerType { NativeType = type, VarName = varname }; - if ( basicType == "int32" || basicType == "int" ) return new IntType { NativeType = type, VarName = varname }; - if ( basicType == "uint32" ) return new UIntType { NativeType = type, VarName = varname }; - if ( basicType == "unsigned int" ) return new UIntType { NativeType = type, VarName = varname }; - 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 }; - - // DANGER DANGER Danger - if ( basicType == "size_t" ) return new ULongType { NativeType = type, VarName = varname }; - if ( basicType == "intptr_t" ) return new LongType { NativeType = type, VarName = varname }; - if ( basicType == "ptrdiff_t" ) return new LongType { 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.EndsWith( "_t" ) ) return new StructType { NativeType = type, VarName = varname, StructName = basicType }; - 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.StartsWith( "E" ) && char.IsUpper( basicType[1] ) ) return new EnumType { NativeType = type.Substring( 1 ), VarName = varname }; - - return new BaseType { NativeType = type, VarName = varname }; - } - - public virtual bool ShouldSkipAsArgument => false; - - public virtual string AsNativeArgument() => AsArgument(); - public virtual string AsArgument() => IsVector ? $"[In,Out] {Ref}{TypeName.Trim( '*', ' ' )}[] {VarName}" : $"{Ref}{TypeName.Trim( '*', ' ' )} {VarName}"; - public virtual string AsCallArgument() => $"{Ref}{VarName}"; - - public virtual string Return( string varname ) => $"return {varname};"; - public virtual string ReturnAttribute => null; - - public virtual string ReturnType => TypeName; - - public virtual string Ref => !IsVector && NativeType.EndsWith( "*" ) || NativeType.EndsWith( "**" ) || NativeType.Contains( "&" ) ? "ref " : ""; - public virtual bool IsVector - { - get - { - if ( Func == "ReadP2PPacket" ) return false; - if ( Func == "SendP2PPacket" ) return false; - - if ( VarName == "pOut" ) return false; - if ( VarName == "pOutBuffer" ) return false; - if ( VarName == "pubRGB" ) return false; - if ( VarName == "pOutResultHandle" ) return false; - if ( VarName == "pOutDataType" ) return false; - - if ( VarName == "psteamIDClans" ) return true; - if ( VarName == "pScoreDetails" ) return true; - if ( VarName == "prgUsers" ) return true; - if ( VarName == "pBasePrices" ) return true; - if ( VarName == "pCurrentPrices" ) return true; - if ( VarName == "pItemDefIDs" ) return true; - if ( VarName == "handlesOut" ) return true; - if ( VarName == "pDetails" && Func == "GetDownloadedLeaderboardEntry" ) return true; - if ( VarName == "pData" && NativeType.EndsWith( "*" ) && Func.StartsWith( "GetGlobalStatHistory" ) ) return true; - if ( NativeType.EndsWith( "**" ) ) return true; - if ( VarName.StartsWith( "pArray" ) ) return true; - if ( VarName.StartsWith( "punArray" ) ) return true; - - if ( NativeType.EndsWith( "*" ) ) - { - if ( VarName.StartsWith( "pvec" ) ) return true; - if ( VarName.StartsWith( "pub" ) ) return true; - if ( VarName.StartsWith( "pOut" ) ) return true; - } - - return false; - } - } - - - 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}.Fill( {varname} );"; - } - - 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 ) => $"return await {CallResult}.GetResultAsync( {varname} );"; - public override string ReturnType => $"async Task<{CallResult}?>"; -} - -internal class CSteamIdType : BaseType -{ - public override bool IsReturnedWeird => true; -} - -internal class IntType : BaseType -{ - public override string TypeName => $"int"; -} - -internal class UIntType : BaseType -{ - public override string TypeName => $"uint"; -} -internal class UInt8Type : BaseType -{ - public override string TypeName => $"byte"; -} - -internal class UInt16Type : BaseType -{ - public override string TypeName => $"ushort"; -} - -internal class PointerType : BaseType -{ - public override string TypeName => $"IntPtr"; - public override string Ref => ""; -} - -internal class ULongType : BaseType -{ - public override string TypeName => $"ulong"; -} - -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/CodeWriter/Utility.cs b/Generator/CodeWriter/Utility.cs index 308b209..9a7efff 100644 --- a/Generator/CodeWriter/Utility.cs +++ b/Generator/CodeWriter/Utility.cs @@ -57,7 +57,9 @@ namespace Generator case "uint64": return "ulong"; case "uint32": return "uint"; case "int32": return "int"; + case "int32_t": return "int"; case "int64": return "long"; + case "int64_t": return "long"; case "void *": return "IntPtr"; case "uint8 *": return "IntPtr"; case "int16": return "short"; diff --git a/Generator/Generator.csproj b/Generator/Generator.csproj index 009fd46..d9f9a97 100644 --- a/Generator/Generator.csproj +++ b/Generator/Generator.csproj @@ -47,13 +47,18 @@ - - - - - + - + + + + + + + + + + diff --git a/Generator/Program.cs b/Generator/Program.cs index 57f9790..1a23c56 100644 --- a/Generator/Program.cs +++ b/Generator/Program.cs @@ -16,33 +16,12 @@ namespace Generator var content = System.IO.File.ReadAllText( "steam_sdk/steam_api.json" ); var def = Newtonsoft.Json.JsonConvert.DeserializeObject( content ); - AddMissing( def ); - - var parser = new CodeParser( @"steam_sdk" ); - - parser.ParseClasses(); - parser.ExtendDefinition( def ); - Definitions = def; - var generator = new CodeWriter( parser, def ); + var generator = new CodeWriter( def ); generator.ToFolder( "../Facepunch.Steamworks/Generated/" ); } - - private static void AddMissing( SteamApiDefinition output ) - { - var content = System.IO.File.ReadAllText( "steam_api_missing.json" ); - var missing = Newtonsoft.Json.JsonConvert.DeserializeObject( content ); - - output.structs.AddRange( missing.structs ); - output.methods.AddRange( missing.methods ); - - foreach ( var s in output.structs ) - { - if ( s.Fields == null ) s.Fields = new SteamApiDefinition.StructDef.StructFields[0]; - } - } } } diff --git a/Generator/SteamApiDefinition.cs b/Generator/SteamApiDefinition.cs index fa159df..d469a98 100644 --- a/Generator/SteamApiDefinition.cs +++ b/Generator/SteamApiDefinition.cs @@ -9,15 +9,50 @@ namespace Generator { public class SteamApiDefinition { - public class TypeDef + public class Interface { - [JsonProperty( PropertyName = "typedef" )] + [JsonProperty( PropertyName = "classname" )] public string Name { get; set; } - [JsonProperty( PropertyName = "type" )] - public string Type { get; set; } + + [JsonProperty( PropertyName = "version_string" )] + public string VersionString { get; set; } + + public class Method + { + public string Desc { get; set; } + public string ReturnType { get; set; } + public string CallResult { get; set; } + + public class Param + { + public string ParamType { get; set; } + public string ParamName { get; set; } + } + + public Param[] Params { get; set; } + [JsonProperty( PropertyName = "methodname" )] + public string Name { get; set; } + [JsonProperty( PropertyName = "methodname_flat" )] + public string FlatName { get; set; } + + } + + public Method[] Methods { get; set; } + + + public class Accessor + { + public string Kind { get; set; } + public string Name { get; set; } + public string Name_Flat { get; set; } + } + + public Accessor[] Accessors { get; set; } + } - public List typedefs { get; set; } + public Interface[] Interfaces { get; set; } + public class EnumDef { @@ -37,6 +72,17 @@ namespace Generator public EnumDef[] enums { get; set; } + + public class TypeDef + { + [JsonProperty( PropertyName = "typedef" )] + public string Name { get; set; } + [JsonProperty( PropertyName = "type" )] + public string Type { get; set; } + } + + public List typedefs { get; set; } + public class StructDef { public class StructFields @@ -52,57 +98,49 @@ namespace Generator [JsonProperty( PropertyName = "fields" )] public StructFields[] Fields { get; set; } - public string CallbackId { get; set; } - public bool IsCallResult { get; set; } + public bool IsPack4OnWindows + { + get + { + // 4/8 packing is irrevant to these classes + if ( Name.Contains( "MatchMakingKeyValuePair_t" ) ) return true; + if ( Fields.Any( x => x.Type.Contains( "CSteamID" ) ) ) + return true; - public bool IsPack4OnWindows - { - get - { - // 4/8 packing is irrevant to these classes - if ( Name.Contains( "MatchMakingKeyValuePair_t" ) ) return true; + return false; + } + } - if ( Fields.Any( x => x.Type.Contains( "CSteamID" ) ) ) - return true; - - return false; - } - } + public EnumDef[] Enums { get; set; } } public List structs { get; set; } - public class MethodDef + public class CallbackStructDef : StructDef { - public class ParamType - { - [JsonProperty( PropertyName = "paramname" )] - public string Name { get; set; } - [JsonProperty( PropertyName = "paramtype" )] - public string Type { get; set; } - } - - [JsonProperty( PropertyName = "classname" )] - public string ClassName { get; set; } - [JsonProperty( PropertyName = "methodname" )] - public string Name { get; set; } - [JsonProperty( PropertyName = "returntype" )] - public string ReturnType { get; set; } - [JsonProperty( PropertyName = "params" )] - public ParamType[] Params { get; set; } - - [JsonProperty( PropertyName = "callresult" )] - public string CallResult { get; set; } - - public bool NeedsSelfPointer = true; + [JsonProperty( PropertyName = "callback_id" )] + public int CallbackId { get; set; } } - public List methods { get; set; } + public List callback_structs { get; set; } + + public class Const + { + [JsonProperty( PropertyName = "consttype" )] + public string Type { get; set; } + + [JsonProperty( PropertyName = "constname" )] + public string Name { get; set; } - public Dictionary CallbackIds { get; internal set; } - public Dictionary Defines { get; internal set; } + [JsonProperty( PropertyName = "constval" )] + public string Val { get; set; } + } + + public List Consts { get; set; } } + + } diff --git a/Generator/Types/BaseType.cs b/Generator/Types/BaseType.cs new file mode 100644 index 0000000..ff51c88 --- /dev/null +++ b/Generator/Types/BaseType.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +internal class BaseType +{ + public string VarName; + public string NativeType; + public virtual string TypeName => $"{NativeType}"; + public virtual string TypeNameFrom => TypeName; + + public string Func; + + public static BaseType Parse( string type, string varname = null, string callresult = null ) + { + type = Cleanup.ConvertType( type ); + + if ( varname == "ppOutMessages" ) return new PointerType { NativeType = "void *", VarName = varname }; + if ( type == "SteamAPIWarningMessageHook_t" ) return new PointerType { NativeType = type, VarName = varname }; + + if ( type == "SteamAPICall_t" ) return new SteamApiCallType { NativeType = type, VarName = varname, CallResult = callresult }; + + 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 FetchStringType { NativeType = type, VarName = varname }; + + var basicType = type.Replace( "const ", "" ).Trim( ' ', '*', '&' ); + + if ( basicType == "void" ) return new PointerType { NativeType = type, VarName = varname }; + if ( basicType.StartsWith( "ISteam" ) ) return new PointerType { NativeType = type, VarName = varname }; + if ( basicType == "const void" ) return new PointerType { NativeType = type, VarName = varname }; + if ( basicType == "int32" || basicType == "int" ) return new IntType { NativeType = type, VarName = varname }; + if ( basicType == "uint" ) return new UIntType { NativeType = type, VarName = varname }; + 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 }; + + // DANGER DANGER Danger + if ( basicType == "intptr_t" ) return new PointerType { 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 }; + + // + // 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 }; + } + + public virtual bool ShouldSkipAsArgument => false; + + public virtual string AsNativeArgument() => AsArgument(); + public virtual string AsArgument() => IsVector ? $"[In,Out] {Ref}{TypeName.Trim( '*', ' ', '&' )}[] {VarName}" : $"{Ref}{TypeName.Trim( '*', ' ', '&' )} {VarName}"; + public virtual string AsCallArgument() => $"{Ref}{VarName}"; + + public virtual string Return( string varname ) => $"return {varname};"; + public virtual string ReturnAttribute => null; + + public virtual string ReturnType => TypeName; + + public virtual string Ref => !IsVector && NativeType.EndsWith( "*" ) || NativeType.EndsWith( "**" ) || NativeType.Contains( "&" ) ? "ref " : ""; + public virtual bool IsVector + { + get + { + if ( Func == "ReadP2PPacket" ) return false; + if ( Func == "SendP2PPacket" ) return false; + if ( VarName == "pOutMessageNumber" ) return false; + if ( VarName == "pOptions" ) return true; + + if ( VarName == "pOut" ) return false; + if ( VarName == "pOutBuffer" ) return false; + if ( VarName == "pubRGB" ) return false; + if ( VarName == "pOutResultHandle" ) return false; + if ( VarName == "pOutDataType" ) return false; + + if ( VarName == "psteamIDClans" ) return true; + if ( VarName == "pScoreDetails" ) return true; + if ( VarName == "prgUsers" ) return true; + if ( VarName == "pBasePrices" ) return true; + if ( VarName == "pCurrentPrices" ) return true; + if ( VarName == "pItemDefIDs" ) return true; + if ( VarName == "handlesOut" ) return true; + if ( VarName == "pDetails" && Func == "GetDownloadedLeaderboardEntry" ) return true; + if ( VarName == "pData" && NativeType.EndsWith( "*" ) && Func.StartsWith( "GetGlobalStatHistory" ) ) return true; + if ( NativeType.EndsWith( "**" ) ) return true; + if ( VarName.StartsWith( "pArray" ) ) return true; + if ( VarName.StartsWith( "punArray" ) ) return true; + + if ( NativeType.EndsWith( "*" ) ) + { + if ( VarName.StartsWith( "pvec" ) ) return true; + if ( VarName.StartsWith( "pub" ) ) return true; + if ( VarName.StartsWith( "pOut" ) ) return true; + } + + return false; + } + } + + + public virtual bool IsVoid => false; +} + +internal class IntType : BaseType +{ + public override string TypeName => $"int"; +} + +internal class UIntType : BaseType +{ + public override string TypeName => $"uint"; +} +internal class UInt8Type : BaseType +{ + public override string TypeName => $"byte"; +} + +internal class UInt16Type : BaseType +{ + public override string TypeName => $"ushort"; +} + +internal class UIntPtrType : BaseType +{ + public override string TypeName => $"UIntPtr"; +} + +internal class PointerType : BaseType +{ + public override string TypeName => $"IntPtr"; + public override string Ref => ""; +} + +internal class ULongType : BaseType +{ + public override string TypeName => $"ulong"; +} + +internal class LongType : BaseType +{ + public override string TypeName => $"long"; +} 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 diff --git a/Generator/steam_api_missing.json b/Generator/steam_api_missing.json deleted file mode 100644 index c058d0d..0000000 --- a/Generator/steam_api_missing.json +++ /dev/null @@ -1,450 +0,0 @@ -{ - "structs": [ - - - - { - "struct": "AvailableBeaconLocationsUpdated_t", - "fields": [ ] - }, - - { - "struct": "ActiveBeaconsUpdated_t", - "fields": [ ] - }, - - { - "struct": "PlaybackStatusHasChanged_t", - "fields": [ ] - }, - - { - "struct": "BroadcastUploadStart_t", - "fields": [ ] - }, - - { - "struct": "NewUrlLaunchParameters_t", - "fields": [ ] - }, - - { - "struct": "ItemInstalled_t", - "fields": [ - { - "fieldname": "m_unAppID", - "fieldtype": "AppId_t" - }, - { - "fieldname": "m_nPublishedFileId", - "fieldtype": "PublishedFileId_t" - } - ] - }, - - { - "struct": "SteamNetConnectionStatusChangedCallback_t", - "fields": [ - { - "fieldname": "m_hConn", - "fieldtype": "HSteamNetConnection" - }, - { - "fieldname": "m_info", - "fieldtype": "SteamNetConnectionInfo_t" - }, - { - "fieldname": "m_eOldState", - "fieldtype": "ESteamNetworkingConnectionState" - } - ] - }, - - { - "struct": "InputAnalogActionData_t", - "fields": [ - { - "fieldname": "eMode", - "fieldtype": "EInputSourceMode" - }, - { - "fieldname": "x", - "fieldtype": "float" - }, - { - "fieldname": "y", - "fieldtype": "float" - }, - { - "fieldname": "bActive", - "fieldtype": "bool" - } - ] - }, - - { - "struct": "InputMotionData_t", - "fields": [ - { - "fieldname": "rotQuatX", - "fieldtype": "float" - }, - - { - "fieldname": "rotQuatY", - "fieldtype": "float" - }, - - { - "fieldname": "rotQuatZ", - "fieldtype": "float" - }, - - { - "fieldname": "rotQuatW", - "fieldtype": "float" - }, - - { - "fieldname": "posAccelX", - "fieldtype": "float" - }, - - { - "fieldname": "posAccelY", - "fieldtype": "float" - }, - - { - "fieldname": "posAccelZ", - "fieldtype": "float" - }, - - { - "fieldname": "rotVelX", - "fieldtype": "float" - }, - - { - "fieldname": "rotVelY", - "fieldtype": "float" - }, - - { - "fieldname": "rotVelZ", - "fieldtype": "float" - } - - ] - }, - - { - "struct": "InputDigitalActionData_t", - "fields": [ - { - "fieldname": "bState", - "fieldtype": "bool" - }, - { - "fieldname": "bActive", - "fieldtype": "bool" - } - ] - }, - - { - "struct": "SteamInventoryDefinitionUpdate_t" - }, - - { - "struct": "SteamParentalSettingsChanged_t" - }, - - { - "struct": "SteamServersConnected_t" - }, - { - "struct": "NewLaunchQueryParameters_t" - }, - - { - "struct": "GCMessageAvailable_t", - "fields": [ - { - "fieldname": "m_nMessageSize", - "fieldtype": "uint32" - } - ] - }, - { - "struct": "GCMessageFailed_t" - }, - { - "struct": "ScreenshotRequested_t" - }, - { - "struct": "LicensesUpdated_t" - }, - { - "struct": "SteamShutdown_t" - }, - { - "struct": "IPCountry_t" - }, - { - "struct": "IPCFailure_t", - "fields": [ - { - "fieldname": "m_eFailureType", - "fieldtype": "uint8" - } - ] - } - ], - - "methods": - [ - { - "classname": "SteamApi", - "methodname": "SteamAPI_Init", - "returntype": "bool", - "NeedsSelfPointer": false - }, - - { - "classname": "SteamApi", - "methodname": "SteamAPI_RunCallbacks", - "returntype": "void", - "NeedsSelfPointer": false - }, - - - { - "classname": "SteamApi", - "methodname": "SteamGameServer_RunCallbacks", - "returntype": "void", - "NeedsSelfPointer": false - }, - - - { - "classname": "SteamApi", - "methodname": "SteamAPI_RegisterCallback", - "returntype": "void", - "NeedsSelfPointer": false, - "params": - [ - { - "paramname": "pCallback", - "paramtype": "void *" - }, - - { - "paramname": "callback", - "paramtype": "int" - } - ] - }, - - - { - "classname": "SteamApi", - "methodname": "SteamAPI_UnregisterCallback", - "returntype": "void", - "NeedsSelfPointer": false, - "params": - [ - { - "paramname": "pCallback", - "paramtype": "void *" - } - ] - }, - - - { - "NeedsSelfPointer": false, - "classname": "SteamApi", - "methodname": "SteamAPI_RegisterCallResult", - "returntype": "void", - "params": - [ - { - "paramname": "pCallback", - "paramtype": "void *" - }, - { - "paramname": "callback", - "paramtype": "SteamAPICall_t" - } - ] - }, - - - { - "NeedsSelfPointer": false, - "classname": "SteamApi", - "methodname": "SteamAPI_UnregisterCallResult", - "returntype": "void", - "params": - [ - { - "paramname": "pCallback", - "paramtype": "void *" - }, - { - "paramname": "callback", - "paramtype": "SteamAPICall_t" - } - ] - }, - - - { - "NeedsSelfPointer": false, - "classname": "SteamApi", - "methodname": "SteamInternal_GameServer_Init", - "returntype": "bool", - "params": - [ - { - "paramname": "unIP", - "paramtype": "uint32" - }, - { - "paramname": "usPort", - "paramtype": "uint16" - }, - { - "paramname": "usGamePort", - "paramtype": "uint16" - }, - { - "paramname": "usQueryPort", - "paramtype": "uint16" - }, - { - "paramname": "eServerMode", - "paramtype": "int" - }, - { - "paramname": "pchVersionString", - "paramtype": "const char *" - } - ] - }, - - { - "NeedsSelfPointer": false, - "classname": "SteamApi", - "methodname": "SteamInternal_FindOrCreateUserInterface", - "returntype": "void *", - "params": - [ - { - "paramname": "steamuser", - "paramtype": "int32" - }, - { - "paramname": "versionname", - "paramtype": "const char *" - } - ] - }, - - { - "NeedsSelfPointer": false, - "classname": "SteamApi", - "methodname": "SteamInternal_FindOrCreateGameServerInterface", - "returntype": "void *", - "params": - [ - { - "paramname": "steamuser", - "paramtype": "int32" - }, - { - "paramname": "versionname", - "paramtype": "const char *" - } - ] - }, - - - { - "NeedsSelfPointer": false, - "classname": "SteamApi", - "methodname": "SteamAPI_Shutdown", - "returntype": "void" - }, - - - { - "NeedsSelfPointer": false, - "classname": "SteamApi", - "methodname": "SteamGameServer_Shutdown", - "returntype": "void" - }, - - - { - "NeedsSelfPointer": false, - "classname": "SteamApi", - "methodname": "SteamAPI_GetHSteamUser", - "returntype": "HSteamUser" - }, - - - { - "NeedsSelfPointer": false, - "classname": "SteamApi", - "methodname": "SteamAPI_GetHSteamPipe", - "returntype": "HSteamPipe" - }, - - - { - "NeedsSelfPointer": false, - "classname": "SteamApi", - "methodname": "SteamGameServer_GetHSteamUser", - "returntype": "HSteamUser" - }, - - - { - "NeedsSelfPointer": false, - "classname": "SteamApi", - "methodname": "SteamGameServer_GetHSteamPipe", - "returntype": "HSteamPipe" - }, - - - { - "NeedsSelfPointer": false, - "classname": "SteamApi", - "methodname": "SteamInternal_CreateInterface", - "returntype": "void *", - "params": - [ - { - "paramname": "version", - "paramtype": "const char *" - } - ] - }, - - { - "NeedsSelfPointer": false, - "classname": "SteamApi", - "methodname": "SteamAPI_RestartAppIfNecessary", - "returntype": "bool", - "params": - [ - { - "paramname": "unOwnAppID", - "paramtype": "uint32" - } - ] - } - ] - } \ No newline at end of file