Files
DepotDownloaderMod/Resources/SteamLanguageParser/Generator/CSharpGen.cs

586 lines
22 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SteamLanguageParser
{
class CSharpGen : ICodeGen
{
private static Dictionary<String, String> readerTypeMap = new Dictionary<String, String>
{
{"byte", "Byte"},
{"short", "Int16"},
{"ushort", "UInt16"},
{"int", "Int32"},
{"uint", "UInt32"},
{"long", "Int64"},
{"ulong", "UInt64"},
{"char", "Char"},
};
public void EmitNamespace(StringBuilder sb, bool end, string nspace)
{
if (end)
{
sb.AppendLine("}");
sb.AppendLine( "#pragma warning restore 1591" );
}
else
{
sb.AppendLine( "#pragma warning disable 1591" ); // this will hide "Missing XML comment for publicly visible type or member 'Type_or_Member'"
sb.AppendLine("using System;");
sb.AppendLine("using System.IO;");
sb.AppendLine( "using System.Runtime.InteropServices;" );
sb.AppendLine();
sb.AppendLine( string.Format( "namespace {0}", nspace ) );
sb.AppendLine("{");
}
}
public void EmitSerialBase(StringBuilder sb, int level, bool supportsGC)
{
string padding = new String('\t', level);
sb.AppendLine(padding + "public interface ISteamSerializable");
sb.AppendLine(padding + "{");
sb.AppendLine(padding + "\tvoid Serialize(Stream stream);");
sb.AppendLine(padding + "\tvoid Deserialize( Stream stream );");
sb.AppendLine(padding + "}");
sb.AppendLine(padding + "public interface ISteamSerializableHeader : ISteamSerializable");
sb.AppendLine(padding + "{");
sb.AppendLine(padding + "\tvoid SetEMsg( EMsg msg );");
sb.AppendLine(padding + "}");
sb.AppendLine(padding + "public interface ISteamSerializableMessage : ISteamSerializable");
sb.AppendLine(padding + "{");
sb.AppendLine(padding + "\tEMsg GetEMsg();");
sb.AppendLine(padding + "}");
if ( supportsGC )
{
sb.AppendLine( padding + "public interface IGCSerializableHeader : ISteamSerializable" );
sb.AppendLine( padding + "{" );
sb.AppendLine( padding + "\tvoid SetEMsg( uint msg );" );
sb.AppendLine( padding + "}" );
sb.AppendLine( padding + "public interface IGCSerializableMessage : ISteamSerializable" );
sb.AppendLine( padding + "{" );
sb.AppendLine( padding + "\tuint GetEMsg();" );
sb.AppendLine( padding + "}" );
}
sb.AppendLine();
}
public string EmitType(Symbol sym)
{
if (sym is WeakSymbol)
{
WeakSymbol wsym = sym as WeakSymbol;
return wsym.Identifier;
}
else if (sym is StrongSymbol)
{
StrongSymbol ssym = sym as StrongSymbol;
if (ssym.Prop == null)
{
return ssym.Class.Name;
}
else
{
return ssym.Class.Name + "." + ssym.Prop.Name;
}
}
return "INVALID";
}
public string GetUpperName(string name)
{
return name.Substring(0, 1).ToUpper() + name.Remove(0, 1);
}
public void EmitNode(Node n, StringBuilder sb, int level)
{
if (n is ClassNode)
{
EmitClassNode(n as ClassNode, sb, level);
}
else if (n is EnumNode)
{
EmitEnumNode(n as EnumNode, sb, level);
}
}
private void EmitEnumNode(EnumNode enode, StringBuilder sb, int level)
{
string padding = new String('\t', level);
if ( enode.Flags == "flags" )
sb.AppendLine( padding + "[Flags]" );
if ( enode.Type != null )
{
sb.AppendLine( padding + "public enum " + enode.Name + " : " + EmitType( enode.Type ) );
}
else
{
sb.AppendLine( padding + "public enum " + enode.Name );
}
sb.AppendLine( padding + "{" );
string lastValue = "0";
foreach (PropNode prop in enode.childNodes)
{
lastValue = EmitType(prop.Default);
sb.AppendLine(padding + "\t" + prop.Name + " = " + lastValue + ",");
}
sb.AppendLine(padding + "}");
}
private void EmitClassNode(ClassNode cnode, StringBuilder sb, int level)
{
EmitClassDef(cnode, sb, level, false);
EmitClassIdentity(cnode, sb, level + 1);
int baseSize = EmitClassProperties(cnode, sb, level + 1);
EmitClassConstructor(cnode, sb, level + 1);
EmitClassSerializer(cnode, sb, level + 1, baseSize);
EmitClassDeserializer(cnode, sb, level + 1, baseSize);
EmitClassDef(cnode, sb, level, true);
}
private void EmitClassDef(ClassNode cnode, StringBuilder sb, int level, bool end)
{
string padding = new String('\t', level);
if (end)
{
sb.AppendLine(padding + "}");
sb.AppendLine();
return;
}
string parent = "ISteamSerializable";
if (cnode.Ident != null)
{
if ( cnode.Name.Contains( "MsgGC" ) )
{
parent = "IGCSerializableMessage";
}
else
{
parent = "ISteamSerializableMessage";
}
}
else if (cnode.Name.Contains("Hdr"))
{
if ( cnode.Name.Contains( "MsgGC" ) )
parent = "IGCSerializableHeader";
else
parent = "ISteamSerializableHeader";
}
if ( cnode.Name.Contains( "Hdr" ) )
{
sb.AppendLine( padding + "[StructLayout( LayoutKind.Sequential )]" );
}
sb.AppendLine(padding + "public class " + cnode.Name + " : " + parent);
sb.AppendLine(padding + "{");
}
private void EmitClassIdentity(ClassNode cnode, StringBuilder sb, int level)
{
string padding = new String('\t', level);
if (cnode.Ident != null)
{
if ( cnode.Name.Contains( "MsgGC" ) )
{
sb.AppendLine( padding + "public uint GetEMsg() { return " + EmitType( cnode.Ident ) + "; }" );
sb.AppendLine();
}
else
{
sb.AppendLine( padding + "public EMsg GetEMsg() { return " + EmitType( cnode.Ident ) + "; }" );
sb.AppendLine();
}
}
else if (cnode.Name.Contains("Hdr"))
{
if ( cnode.Name.Contains( "MsgGC" ) )
{
if ( cnode.childNodes.Find( node => node.Name == "msg" ) != null )
{
sb.AppendLine( padding + "public void SetEMsg( uint msg ) { this.Msg = msg; }" );
sb.AppendLine();
}
else
{
// this is required for a gc header which doesn't have an emsg
sb.AppendLine( padding + "public void SetEMsg( uint msg ) { }" );
sb.AppendLine();
}
}
else
{
sb.AppendLine( padding + "public void SetEMsg( EMsg msg ) { this.Msg = msg; }" );
sb.AppendLine();
}
}
}
private int EmitClassProperties(ClassNode cnode, StringBuilder sb, int level)
{
string padding = new String('\t', level);
int baseClassSize = 0;
if (cnode.Parent != null)
{
sb.AppendLine(padding + "public " + EmitType(cnode.Parent) + " Header { get; set; }");
}
foreach (PropNode prop in cnode.childNodes)
{
string typestr = EmitType(prop.Type);
string propName = GetUpperName(prop.Name);
if (prop.Flags != null && prop.Flags == "const")
{
sb.AppendLine(padding + "public static readonly " + typestr + " " + propName + " = " + EmitType(prop.Default) + ";");
continue;
}
int size = CodeGenerator.GetTypeSize(prop);
baseClassSize += size;
sb.AppendLine( padding + "// Static size: " + size);
if (prop.Flags != null && prop.Flags == "steamidmarshal" && typestr == "ulong")
{
sb.AppendLine( padding + string.Format( "private {0} {1};", typestr, prop.Name ) );
sb.AppendLine( padding + "public SteamID " + propName + " { get { return new SteamID( " + prop.Name + " ); } set { " + prop.Name + " = value.ConvertToUInt64(); } }");
}
else if ( prop.Flags != null && prop.Flags == "boolmarshal" && typestr == "byte" )
{
sb.AppendLine( padding + string.Format( "private {0} {1};", typestr, prop.Name ) );
sb.AppendLine( padding + "public bool " + propName + " { get { return ( " + prop.Name + " == 1 ); } set { " + prop.Name + " = ( byte )( value ? 1 : 0 ); } }" );
}
else if ( prop.Flags != null && prop.Flags == "gameidmarshal" && typestr == "ulong" )
{
sb.AppendLine( padding + string.Format( "private {0} {1};", typestr, prop.Name ) );
sb.AppendLine( padding + "public GameID " + propName + " { get { return new GameID( " + prop.Name + " ); } set { " + prop.Name + " = value.ToUInt64(); } }" );
}
else
{
int temp;
if ( !String.IsNullOrEmpty( prop.FlagsOpt ) && Int32.TryParse( prop.FlagsOpt, out temp ) )
{
typestr += "[]";
}
sb.AppendLine( padding + "public " + typestr + " " + propName + " { get; set; }" );
}
}
sb.AppendLine();
return baseClassSize;
}
private void EmitClassConstructor(ClassNode cnode, StringBuilder sb, int level)
{
string padding = new String('\t', level);
sb.AppendLine(padding + "public " + cnode.Name + "()");
sb.AppendLine(padding + "{");
if (cnode.Parent != null)
{
sb.AppendLine(padding + "\tHeader = new " + EmitType(cnode.Parent) + "();");
sb.AppendLine(padding + "\tHeader.Msg = GetEMsg();");
}
foreach (PropNode prop in cnode.childNodes)
{
Symbol defsym = prop.Default;
string defflags = prop.Flags;
string symname = GetUpperName(prop.Name);
string ctor = EmitType(defsym);
if (defflags != null && defflags == "proto")
{
ctor = "new " + EmitType(prop.Type) + "()";
}
else if (defsym == null)
{
if ( !String.IsNullOrEmpty( prop.FlagsOpt ) )
{
ctor = "new " + EmitType( prop.Type ) + "[" + CodeGenerator.GetTypeSize( prop ) + "]";
}
else
{
ctor = "0";
}
}
if (defflags != null && ( defflags == "steamidmarshal" || defflags == "gameidmarshal" || defflags == "boolmarshal" ))
{
symname = prop.Name;
}
else if ( defflags != null && defflags == "const" )
{
continue;
}
sb.AppendLine(padding + "\t" + symname + " = " + ctor + ";");
}
sb.AppendLine(padding + "}");
}
private void EmitClassSerializer(ClassNode cnode, StringBuilder sb, int level, int baseSize)
{
string padding = new String('\t', level);
sb.AppendLine();
sb.AppendLine(padding + "public void Serialize(Stream stream)");
sb.AppendLine(padding + "{");
// first emit variable length members
List<String> varLengthProps = new List<String>();
List<String> openedStreams = new List<String>();
varLengthProps.Add(baseSize.ToString());
if (cnode.Parent != null)
{
sb.AppendLine(padding + "\tHeader.Serialize(stream);");
varLengthProps.Add("(int)msHeader.Length");
openedStreams.Add("msHeader");
sb.AppendLine();
}
foreach (PropNode prop in cnode.childNodes)
{
string typestr = EmitType(prop.Type);
int size = CodeGenerator.GetTypeSize(prop);
if (size == 0)
{
if (prop.Flags != null && prop.Flags == "proto")
{
if (baseSize == 0)
{
// early exit
sb.AppendLine(padding + "\tProtoBuf.Serializer.Serialize<" + typestr + ">(stream, " + GetUpperName(prop.Name) + ");");
sb.AppendLine(padding + "}");
return;
}
sb.AppendLine(padding + "\tMemoryStream ms" + GetUpperName(prop.Name) + " = new MemoryStream();");
sb.AppendLine(padding + "\tProtoBuf.Serializer.Serialize<" + typestr + ">(ms" + GetUpperName(prop.Name) + ", " + GetUpperName(prop.Name) + ");");
if (prop.FlagsOpt != null)
{
sb.AppendLine(padding + "\t" + GetUpperName(prop.FlagsOpt) + " = (int)ms" + GetUpperName(prop.Name) + ".Length;");
}
//sb.AppendLine(padding + "\tms" + GetUpperName(prop.Name) + ".Seek( 0, SeekOrigin.Begin );");
}
else
{
sb.AppendLine(padding + "\tMemoryStream ms" + GetUpperName(prop.Name) + " = " + GetUpperName(prop.Name) + ".serialize();");
}
varLengthProps.Add( "(int)ms" + GetUpperName(prop.Name) + ".Length" );
openedStreams.Add( "ms" + GetUpperName(prop.Name) );
}
}
//sb.AppendLine(padding + "\tBinaryWriterEx bw = new BinaryWriterEx( stream );");
//sb.AppendLine();
sb.AppendLine(padding + "\tBinaryWriter bw = new BinaryWriter( stream );");
sb.AppendLine();
if (cnode.Parent != null)
{
sb.AppendLine(padding + "\tmsHeader.CopyTo( msBuffer );");
}
// next emit writers
foreach (PropNode prop in cnode.childNodes)
{
string typecast = "";
string propName = GetUpperName(prop.Name);
if (prop.Type is StrongSymbol && ((StrongSymbol)prop.Type).Class is EnumNode)
{
EnumNode enode = ((StrongSymbol)prop.Type).Class as EnumNode;
if (enode.Type is WeakSymbol)
typecast = "(" + ((WeakSymbol)enode.Type).Identifier + ")";
else
typecast = "(int)";
}
if (prop.Flags != null)
{
if ( prop.Flags == "steamidmarshal" || prop.Flags == "gameidmarshal" || prop.Flags == "boolmarshal" )
{
propName = prop.Name;
}
else if ( prop.Flags == "proto" )
{
sb.AppendLine( padding + "\tbw.Write( ms" + propName + ".ToArray() );" );
continue;
}
else if ( prop.Flags == "const" )
{
continue;
}
}
if (prop.Flags == "protomask")
{
propName = "MsgUtil.MakeMsg( " + propName + ", true )";
}
else if ( prop.Flags == "protomaskgc" )
{
propName = "MsgUtil.MakeGCMsg( " + propName + ", true )";
}
sb.AppendLine(padding + "\tbw.Write( " + typecast + propName + " );");
}
sb.AppendLine();
foreach (String stream in openedStreams)
{
sb.AppendLine(padding + "\t" + stream + ".Close();");
}
//sb.AppendLine();
//sb.AppendLine(padding + "\tmsBuffer.Seek( 0, SeekOrigin.Begin );");
sb.AppendLine(padding + "}");
}
private void EmitClassSize( ClassNode cnode, StringBuilder sb, int level )
{
}
private void EmitClassDeserializer(ClassNode cnode, StringBuilder sb, int level, int baseSize)
{
string padding = new String('\t', level);
sb.AppendLine();
sb.AppendLine(padding + "public void Deserialize( Stream stream )");
sb.AppendLine(padding + "{");
if (baseSize > 0)
{
sb.AppendLine(padding + "\tBinaryReader br = new BinaryReader( stream );");
sb.AppendLine();
}
if (cnode.Parent != null)
{
sb.AppendLine(padding + "\tHeader.Deserialize( stream );");
}
foreach (PropNode prop in cnode.childNodes)
{
string typestr = EmitType(prop.Type);
int size = CodeGenerator.GetTypeSize(prop);
string defflags = prop.Flags;
string symname = GetUpperName(prop.Name);
if ( defflags != null && ( defflags == "steamidmarshal" || defflags == "gameidmarshal" || defflags == "boolmarshal" ) )
{
symname = prop.Name;
}
else if ( defflags != null && defflags == "const" )
{
continue;
}
if (size == 0)
{
if (prop.Flags != null && prop.Flags == "proto")
{
if (prop.FlagsOpt != null)
{
sb.AppendLine(padding + "\tusing( MemoryStream ms" + GetUpperName(prop.Name) + " = new MemoryStream( br.ReadBytes( " + GetUpperName(prop.FlagsOpt) + " ) ) )");
sb.AppendLine(padding + "\t\t" + GetUpperName(prop.Name) + " = ProtoBuf.Serializer.Deserialize<" + typestr + ">( ms" + GetUpperName(prop.Name) + " );");
}
else
{
sb.AppendLine(padding + "\t" + GetUpperName(prop.Name) + " = ProtoBuf.Serializer.Deserialize<" + typestr + ">( stream );");
}
}
else
{
sb.AppendLine(padding + "\t" + GetUpperName(prop.Name) + ".Deserialize( stream );");
}
}
else
{
string typecast = "";
if (!readerTypeMap.ContainsKey(typestr))
{
typecast = "(" + typestr + ")";
typestr = CodeGenerator.GetTypeOfSize(size, SupportsUnsignedTypes());
}
string call = "br.Read" + readerTypeMap[typestr] + "()";
if (!String.IsNullOrEmpty(prop.FlagsOpt))
{
call = "br.Read" + readerTypeMap[typestr] + "s( " + prop.FlagsOpt + " )";
}
if (prop.Flags == "protomask")
{
call = "MsgUtil.GetMsg( (uint)" + call + " )";
}
else if ( prop.Flags == "protomaskgc" )
{
call = "MsgUtil.GetGCMsg( (uint)" + call + " )";
}
sb.AppendLine(padding + "\t" + symname + " = " + typecast + call + ";");
}
}
sb.AppendLine(padding + "}");
}
public bool SupportsUnsignedTypes()
{
return true;
}
public bool SupportsNamespace()
{
return true;
}
}
}