Updated generator to use flat files

This commit is contained in:
Garry Newman 2020-02-22 20:16:04 +00:00
parent 85339a079a
commit ed96cafbe4
28 changed files with 959 additions and 1818 deletions

View File

@ -10,9 +10,15 @@ public static class Cleanup
{ {
public static string ConvertType( string type ) 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( "CSteamID", "SteamId" );
type = type.Replace( "CGameID", "GameId" ); type = type.Replace( "CGameID", "GameId" );
type = type.Replace( "PersonaState", "FriendState" );
type = type.Replace( "AudioPlayback_Status", "MusicStatus" ); type = type.Replace( "AudioPlayback_Status", "MusicStatus" );
type = type.Replace( "AuthSessionResponse", "AuthResponse" ); type = type.Replace( "AuthSessionResponse", "AuthResponse" );
type = type.Replace( "FriendRelationship", "Relationship" ); type = type.Replace( "FriendRelationship", "Relationship" );
@ -27,8 +33,9 @@ public static class Cleanup
type = type.Replace( "SteamItemDef_t", "InventoryDefId" ); type = type.Replace( "SteamItemDef_t", "InventoryDefId" );
type = type.Replace( "ChatRoomEnterResponse", "RoomEnter" ); type = type.Replace( "ChatRoomEnterResponse", "RoomEnter" );
type = type.Replace( "SteamNetworkPingLocation_t", "PingLocation" ); type = type.Replace( "SteamNetworkPingLocation_t", "PingLocation" );
type = type.Replace( "SteamNetworkingConfigValue_t", "NetKeyValue" );
type = type.Replace( "SteamNetworkingConfigValue", "NetConfig" ); type = type.Replace( "SteamNetworkingConfigValue", "NetConfig" );
type = type.Replace( "SteamNetworkingConfigScope", "NetScope" ); type = type.Replace( "SteamNetworkingConfigScope", "NetConfigScope" );
type = type.Replace( "SteamNetworkingConfigDataType", "NetConfigType" ); type = type.Replace( "SteamNetworkingConfigDataType", "NetConfigType" );
type = type.Replace( "HSteamNetConnection", "Connection" ); type = type.Replace( "HSteamNetConnection", "Connection" );
type = type.Replace( "HSteamListenSocket", "Socket" ); type = type.Replace( "HSteamListenSocket", "Socket" );
@ -44,6 +51,15 @@ public static class Cleanup
type = type.Replace( "InputAnalogActionData_t", "AnalogState" ); type = type.Replace( "InputAnalogActionData_t", "AnalogState" );
type = type.Replace( "InputMotionData_t", "MotionState" ); type = type.Replace( "InputMotionData_t", "MotionState" );
type = type.Replace( "MatchMakingKeyValuePair_t", "MatchMakingKeyValuePair" ); 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; return type;
} }
@ -58,6 +74,28 @@ public static class Cleanup
if ( type == "DigitalState" ) return false; if ( type == "DigitalState" ) return false;
if ( type == "MotionState" ) return false; if ( type == "MotionState" ) return false;
if ( type == "MatchMakingKeyValuePair" ) 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; return true;
} }
@ -88,6 +126,9 @@ public static class Cleanup
if ( name == "InputType" ) return "public"; if ( name == "InputType" ) return "public";
if ( name == "InputSourceMode" ) return "public"; if ( name == "InputSourceMode" ) return "public";
if ( name == "UserHasLicenseForAppResult" ) return "public"; if ( name == "UserHasLicenseForAppResult" ) return "public";
if ( name == "PingLocation" ) return "public";
if ( name == "ConnectionState" ) return "public";
if ( name == "SteamNetworkingAvailability" ) return "public";
return "internal"; return "internal";
} }
@ -103,5 +144,17 @@ public static class Cleanup
} }
return false; 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 );
} }
} }

View File

@ -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<string, string> Arguments = new Dictionary<string, string>();
public string ReturnType;
public string CallResult;
}
public List<Function> Functions = new List<Function>();
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}";
}
}
}
}
}
}

View File

@ -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<string, int>();
//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<string, string>();
{
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";
}
}
}
}
}
}

View File

@ -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<Class> Classes = new List<Class>();
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;
}
}
}

View File

@ -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<int>();
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<F{func.Name}>( 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<F{func.Name}>( Marshal.ReadIntPtr( VTable, Platform.MemoryOffset( {windowsLocations[i]} ) ) );" );
}
WriteLine( "#else" );
foreach ( var i in different )
{
var func = clss.Functions[i];
WriteLine( $"_{func.Name} = Marshal.GetDelegateForFunctionPointer<F{func.Name}>( 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<args.Length; i++ )
{
if ( args[i] is StringType )
{
if ( args[i + 1] is IntType || args[i + 1] is UIntType )
{
if ( args[i + 1].Ref == string.Empty )
{
args[i + 1] = new ConstValueType( args[i + 1], "(1024 * 32)" );
}
}
else
{
throw new System.Exception( $"String Builder Next Type Is {args[i+1].GetType()}" );
}
}
}
var argstr = string.Join( ", ", args.Where( x => !x.ShouldSkipAsArgument ).Select( x => x.AsArgument() ) ); ;
var delegateargstr = string.Join( ", ", args.Select( x => x.AsNativeArgument() ) );
var windowsSpecific = NeedsWindowsSpecificFunction( func, returnType, args );
if ( returnType is SteamApiCallType sap )
{
sap.CallResult = func.CallResult;
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();
}
}
}

View File

@ -10,14 +10,14 @@ namespace Generator
{ {
public partial class CodeWriter public partial class CodeWriter
{ {
public static CodeWriter Current { get; private set; }
private SteamApiDefinition def; private SteamApiDefinition def;
public CodeParser Parser;
public CodeWriter( CodeParser parser, SteamApiDefinition def ) public CodeWriter( SteamApiDefinition def )
{ {
Parser = parser;
this.def = def; this.def = def;
Current = this;
WorkoutTypes(); WorkoutTypes();
} }
@ -29,6 +29,14 @@ namespace Generator
Enums(); Enums();
Footer(); Footer();
System.IO.File.WriteAllText( $"{folder}SteamEnums.cs", sb.ToString() ); 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() ); 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(); sb = new StringBuilder();
Header( "Steamworks.Data" ); Header( "Steamworks.Data" );
@ -56,34 +72,15 @@ namespace Generator
} }
{ {
GenerateGlobalFunctions( "SteamAPI", $"{folder}../Generated/SteamAPI.cs" ); // GenerateGlobalFunctions( "SteamAPI", $"{folder}../Generated/SteamAPI.cs" );
GenerateGlobalFunctions( "SteamGameServer", $"{folder}../Generated/SteamGameServer.cs" ); // GenerateGlobalFunctions( "SteamGameServer", $"{folder}../Generated/SteamGameServer.cs" );
GenerateGlobalFunctions( "SteamInternal", $"{folder}../Generated/SteamInternal.cs" ); // GenerateGlobalFunctions( "SteamInternal", $"{folder}../Generated/SteamInternal.cs" );
} }
{ foreach ( var iface in def.Interfaces )
GenerateVTableClass( "ISteamApps", $"{folder}../Generated/Interfaces/ISteamApps.cs" ); {
GenerateVTableClass( "ISteamUtils", $"{folder}../Generated/Interfaces/ISteamUtils.cs" ); GenerateInterface( iface, $"{folder}../Generated/Interfaces/" );
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" );
}
} }
void WorkoutTypes() void WorkoutTypes()
@ -125,5 +122,37 @@ namespace Generator
{ {
EndBlock(); 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;
}
} }
} }

View File

@ -10,17 +10,31 @@ namespace Generator
{ {
private void Constants() 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" ); 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(); EndBlock();
} }

View File

@ -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();
}
}
}

View File

@ -25,55 +25,75 @@ namespace Generator
if ( name[0] == 'E' ) if ( name[0] == 'E' )
name = name.Substring( 1 ); name = name.Substring( 1 );
name = Cleanup.ConvertType( name ); name = Cleanup.ConvertType( name );
if ( !Cleanup.ShouldCreate( name ) ) if ( !Cleanup.ShouldCreate( name ) )
continue; 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++ )
{ {
// var c = o.Values.First().Name[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;
}
foreach ( var entry in o.Values ) foreach ( var entry in o.Values )
{ {
var ename = entry.Name; if ( entry.Name[i] != c )
{
if ( iFinished != int.MaxValue ) iFinished = i;
ename = ename.Substring( iFinished ); break;
}
//
// 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 ( 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();
} }
} }
} }

View File

@ -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();
}
}
}

View File

@ -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<args.Length; i++ )
{
if ( args[i] is FetchStringType )
{
if ( args[i + 1] is IntType || args[i + 1] is UIntType || args[i + 1] is UIntPtrType )
{
if ( args[i + 1].Ref == string.Empty )
{
args[i + 1] = new LiteralType( args[i + 1], "(1024 * 32)" );
}
}
else
{
throw new System.Exception( $"String Builder Next Type Is {args[i+1].GetType()}" );
}
}
}
var argstr = string.Join( ", ", args.Where( x => !x.ShouldSkipAsArgument ).Select( x => x.AsArgument() ) ); ;
var delegateargstr = string.Join( ", ", args.Select( x => x.AsNativeArgument() ) );
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( "/// <summary>" );
WriteLine( $"/// {func.Desc}" );
WriteLine( "/// </summary>" );
}
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();
}
}
}

View File

@ -17,20 +17,6 @@ namespace Generator
private Dictionary<string, TypeDef> TypeDefs = new Dictionary<string, TypeDef>(); private Dictionary<string, TypeDef> TypeDefs = new Dictionary<string, TypeDef>();
//
// 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[] public readonly static string[] ForceLargePackStructs = new string[]
{ {
"LeaderboardEntry_t" "LeaderboardEntry_t"
@ -38,15 +24,10 @@ namespace Generator
void Structs() void Structs()
{ {
var callbackList = new List<SteamApiDefinition.StructDef>();
foreach ( var c in def.structs ) foreach ( var c in def.structs )
{ {
var name = Cleanup.ConvertType( c.Name ); var name = Cleanup.ConvertType( c.Name );
if ( SkipStructs.Contains( c.Name ) )
continue;
if ( !Cleanup.ShouldCreate( name ) ) if ( !Cleanup.ShouldCreate( name ) )
continue; continue;
@ -55,12 +36,10 @@ namespace Generator
int defaultPack = c.IsPack4OnWindows ? 4 : 8; int defaultPack = c.IsPack4OnWindows ? 4 : 8;
var isCallback = !string.IsNullOrEmpty( c.CallbackId ); //
// Main struct
// //
// Main struct WriteLine( $"[StructLayout( LayoutKind.Sequential, Pack = Platform.{(c.IsPack4OnWindows?"StructPackSize": "StructPlatformPackSize")} )]" );
//
WriteLine( $"[StructLayout( LayoutKind.Sequential, Pack = Platform.{(c.IsPack4OnWindows?"StructPackSize": "StructPlatformPackSize")} )]" );
StartBlock( $"{Cleanup.Expose( name )} struct {name}" ); StartBlock( $"{Cleanup.Expose( name )} struct {name}" );
{ {
// //
@ -69,80 +48,12 @@ namespace Generator
StructFields( c.Fields ); StructFields( c.Fields );
WriteLine(); WriteLine();
if ( isCallback ) if ( c.Enums != null )
{ {
WriteLine( "#region SteamCallback" ); foreach ( var e in c.Enums )
{ {
WriteEnum( e, e.Name );
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 );
} }
} }
@ -206,6 +117,13 @@ namespace Generator
WriteLine( $"[MarshalAs(UnmanagedType.ByValArray, SizeConst = {num}, ArraySubType = UnmanagedType.U4)]" ); 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( "[" ) ) if ( t.StartsWith( "float " ) && t.Contains( "[" ) )
{ {
var num = t.Replace( "float", "" ).Trim( '[', ']', ' ' ); var num = t.Replace( "float", "" ).Trim( '[', ']', ' ' );

View File

@ -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<SteamApiDefinition.StructDef>();
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();
}
}
}
}

View File

@ -37,7 +37,10 @@ namespace Generator
{ {
foreach ( var o in def.typedefs.Where( x => !x.Name.Contains( "::" ) ) ) 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 ) ) if ( !Cleanup.ShouldCreate( typeName ) )
continue; continue;
@ -50,7 +53,16 @@ namespace Generator
StartBlock( $"{Cleanup.Expose( typeName )} struct {typeName} : IEquatable<{typeName}>, IComparable<{typeName}>" ); 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();
WriteLine( $"public static implicit operator {typeName}( {ToManagedType( o.Type )} value ) => new {typeName}(){{ Value = value }};" ); 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;" ); WriteLine( $"public static implicit operator {ToManagedType( o.Type )}( {typeName} value ) => value.Value;" );

View File

@ -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;
}

View File

@ -57,7 +57,9 @@ namespace Generator
case "uint64": return "ulong"; case "uint64": return "ulong";
case "uint32": return "uint"; case "uint32": return "uint";
case "int32": return "int"; case "int32": return "int";
case "int32_t": return "int";
case "int64": return "long"; case "int64": return "long";
case "int64_t": return "long";
case "void *": return "IntPtr"; case "void *": return "IntPtr";
case "uint8 *": return "IntPtr"; case "uint8 *": return "IntPtr";
case "int16": return "short"; case "int16": return "short";

View File

@ -47,13 +47,18 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="CodeParser\CodeParser.Class.cs" /> <Compile Include="CodeWriter\Interface.cs" />
<Compile Include="CodeParser\CodeParser.cs" />
<Compile Include="CodeParser\ParseClasses.cs" />
<Compile Include="CodeWriter\GlobalFunctions.cs" />
<Compile Include="CodeWriter\ClassVTable.cs" />
<Compile Include="CodeWriter\Constants.cs" /> <Compile Include="CodeWriter\Constants.cs" />
<Compile Include="CodeWriter\Types\BaseType.cs" /> <Compile Include="CodeWriter\CustomEnums.cs" />
<Compile Include="CodeWriter\StructCallbacks.cs" />
<Compile Include="Types\FetchStringType.cs" />
<Compile Include="Types\ConstCharType.cs" />
<Compile Include="Types\LiteralType.cs" />
<Compile Include="Types\StructType.cs" />
<Compile Include="Types\BoolType.cs" />
<Compile Include="Types\VoidType.cs" />
<Compile Include="Types\SteamApiCallType.cs" />
<Compile Include="Types\BaseType.cs" />
<Compile Include="CodeWriter\Writing.cs" /> <Compile Include="CodeWriter\Writing.cs" />
<Compile Include="CodeWriter\CodeWriter.cs" /> <Compile Include="CodeWriter\CodeWriter.cs" />
<Compile Include="CodeWriter\Enums.cs" /> <Compile Include="CodeWriter\Enums.cs" />

View File

@ -16,33 +16,12 @@ namespace Generator
var content = System.IO.File.ReadAllText( "steam_sdk/steam_api.json" ); var content = System.IO.File.ReadAllText( "steam_sdk/steam_api.json" );
var def = Newtonsoft.Json.JsonConvert.DeserializeObject<SteamApiDefinition>( content ); var def = Newtonsoft.Json.JsonConvert.DeserializeObject<SteamApiDefinition>( content );
AddMissing( def );
var parser = new CodeParser( @"steam_sdk" );
parser.ParseClasses();
parser.ExtendDefinition( def );
Definitions = def; Definitions = def;
var generator = new CodeWriter( parser, def ); var generator = new CodeWriter( def );
generator.ToFolder( "../Facepunch.Steamworks/Generated/" ); 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<SteamApiDefinition>( 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];
}
}
} }
} }

View File

@ -9,15 +9,50 @@ namespace Generator
{ {
public class SteamApiDefinition public class SteamApiDefinition
{ {
public class TypeDef public class Interface
{ {
[JsonProperty( PropertyName = "typedef" )] [JsonProperty( PropertyName = "classname" )]
public string Name { get; set; } 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<TypeDef> typedefs { get; set; } public Interface[] Interfaces { get; set; }
public class EnumDef public class EnumDef
{ {
@ -37,6 +72,17 @@ namespace Generator
public EnumDef[] enums { get; set; } 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<TypeDef> typedefs { get; set; }
public class StructDef public class StructDef
{ {
public class StructFields public class StructFields
@ -52,57 +98,49 @@ namespace Generator
[JsonProperty( PropertyName = "fields" )] [JsonProperty( PropertyName = "fields" )]
public StructFields[] Fields { get; set; } public StructFields[] Fields { get; set; }
public string CallbackId { get; set; } public bool IsPack4OnWindows
public bool IsCallResult { get; set; } {
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 return false;
{ }
get }
{
// 4/8 packing is irrevant to these classes
if ( Name.Contains( "MatchMakingKeyValuePair_t" ) ) return true;
if ( Fields.Any( x => x.Type.Contains( "CSteamID" ) ) ) public EnumDef[] Enums { get; set; }
return true;
return false;
}
}
} }
public List<StructDef> structs { get; set; } public List<StructDef> structs { get; set; }
public class MethodDef public class CallbackStructDef : StructDef
{ {
public class ParamType [JsonProperty( PropertyName = "callback_id" )]
{ public int CallbackId { get; set; }
[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;
} }
public List<MethodDef> methods { get; set; } public List<CallbackStructDef> 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<string, int> CallbackIds { get; internal set; } [JsonProperty( PropertyName = "constval" )]
public Dictionary<string, string> Defines { get; internal set; } public string Val { get; set; }
}
public List<Const> Consts { get; set; }
} }
} }

169
Generator/Types/BaseType.cs Normal file
View File

@ -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";
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/// <summary>
/// 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.
/// </summary>
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 )]";
}

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/// <summary>
/// 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.
/// </summary>
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 => "";
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/// <summary>
/// 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.
/// </summary>
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 => "";
}

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/// <summary>
/// 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
/// </summary>
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;
}

View File

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/// <summary>
/// Functions returning SteamAPICall_t are converted to be asyncronhous
/// and return a generic CallResult struct with the T of the result type.
/// </summary>
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";
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/// <summary>
/// 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)
/// </summary>
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 );
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/// <summary>
/// Nothing - just priny void
/// </summary>
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;
}

View File

@ -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"
}
]
}
]
}