mirror of
https://github.com/SteamAutoCracks/DepotDownloaderMod.git
synced 2026-02-13 05:10:56 +01:00
This should be considered experimental and will most likely contain breaking changes for all GC related code. --HG-- branch : gc
586 lines
22 KiB
C#
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.Name.Contains( "Flag" ) )
|
|
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;
|
|
}
|
|
|
|
}
|
|
}
|