// A method was called by our inspected method. We use the collected information (our environment) // to extract information about the type (and fields). public static void SerializeOnCall(CallInfo info, List <byte> writtenBytes, List <IRClassProperty> properties) { if (!info.Method.Name.StartsWith("Write") || info.Method.Name.StartsWith("WriteTo")) { // We are in no relevant method. return; } if (info.Method.Name.Equals("WriteRawTag")) { // Used to write tag information. return; } // Name of the type of the proto equivalent of the property. var type = info.Method.Name.Substring(5); // Name of the property var propName = info.Arguments[1].ToString(); propName = propName.Substring(propName.IndexOf("get_") + 4); // Cut of parenthesis. propName = propName.Substring(0, propName.Length - 2); // Locate property from property list var property = properties.First(p => p.Name.Equals(propName)); // Get more specific type. var specificType = InspectorTools.LiteralTypeMapper(type); property.Type = specificType; // Label and fieldTag are already set! }
// Read out static constructor for setters off _repeated_XXX_codec fields. public static void StaticCctorOnStore(StoreInfo info, List <IRClassProperty> properties, List <ulong> recordedTags) { if (!info.Field.Name.StartsWith("_repeated_")) { return; } // Extract the property name of the field we are storing data into. var property = info.Field.Name.Substring(10); // Cut off '_repeated_' from front property = property.Substring(0, property.Length - 6); // Cut off '_codec' at the end // Uppercase first character of property name, because .. inconsistencies.. property = Char.ToUpper(property[0]) + property.Substring(1); // Find property. IRClassProperty prop; var propEnum = properties.Where(p => p.Name.Equals(property)); if (propEnum.Any()) { prop = propEnum.First(); } else { // Inconsistency happened in naming the backing field for repeated properties. // Retry with a trimmed property name. prop = properties.First(p => p.Name.Trim('_').Equals(property)); } // POP matching tag for this property var tag = recordedTags[0]; recordedTags.RemoveAt(0); // Split tag into bytes. var bytes = new List <byte>(); // Bytes must be gotten in Little endian format! // Only send in the minimum of bytes. for (int i = 0; i < sizeof(ulong); ++i) { // Shift bits by bytesize (8) and take last 8 bits. var b = (byte)((tag >> (8 * i)) & 0xff); // Push the result onto the byte list. bytes.Add(b); // If the MSB of this byte is 0, break the loop. // => this guarantees minimal written bytes. if (0 == (b & 0x80)) { break; } } // bytes contains the written tag, split per 8 bits, in little endian. // Check if the field is packed. prop.Options.IsPacked = InspectorTools.TagToPackedSpecifier(bytes, prop.Type); return; }
// Get all properties from the type we are analyzing. public static List <IRClassProperty> ExtractClassProperties(TypeDefinition _subjectClass, out List <TypeDefinition> references) { // Will contain all typedefinitions of types referenced by this class. references = new List <TypeDefinition>(); // All properties for the given class definition. List <IRClassProperty> properties = new List <IRClassProperty>(); // Property != field // Properties expose fields by providing a getter/setter. // The properties are public accessible data, and these are the things that map // to protobuffer files. foreach (var property in _subjectClass.Properties) { // Property must have a setter method, otherwise it wouldn't be related to ProtoBuffers schema. if (property.SetMethod == null) { continue; } // Object which the current property references. TypeDefinition refDefinition; // Set of field (proto) options. IRClassProperty.ILPropertyOptions options = new IRClassProperty.ILPropertyOptions(); // Default to invalid field. options.Label = FieldLabel.INVALID; // IR type of the property. PropertyTypeKind propType = InspectorTools.DefaultTypeMapper(property, out refDefinition); // IR object - reference placeholder for the IR Class. IRTypeNode irReference = null; if (propType == PropertyTypeKind.TYPE_REF) { irReference = InspectorTools.ConstructIRType(refDefinition); // Also save the reference typedefinition for the caller to process. references.Add(refDefinition); } // Construct IR property and store. var prop = new IRClassProperty { Name = property.Name, Type = propType, ReferencedType = irReference, Options = options, }; properties.Add(prop); } return(properties); }
// Get all properties from the type we are analyzing. public static List <IRClassProperty> ExtractClassProperties(TypeDefinition _subjectClass, out List <TypeDefinition> references) { // Contains all references (TypeDefinitions) that are referenced by this class. references = new List <TypeDefinition>(); // All properties for the given class. List <IRClassProperty> properties = new List <IRClassProperty>(); // Propertye != field; see SilentOrbitInspector.ExtractClassproperties(..) foreach (var property in _subjectClass.Properties) { // Default to OPTIONAL label. The Google protobuffer compiler is written for // proto3 syntax, which does not allow REQUIRED. // Everything is implicitly OPTIONAL, but OPTIONAL fields who have the default value // will not be written onto the wire. // CARE MUST BE TAKEN to provide all necessary values when using these decompiled proto files. FieldLabel label = FieldLabel.OPTIONAL; // Each schema related property must have a set method, but repeated fields don't have // a set method. We must figure out if the property is lib related or schema related! if (property.SetMethod == null) { // Repeated field have propertyType RepeatedField<X>. if (property.PropertyType.Name.Contains("RepeatedField") == true && property.PropertyType.IsGenericInstance) { // This field must be analyzed. label = FieldLabel.REPEATED; } else { continue; } } // Object which the current property references. TypeDefinition refDefinition; // Field options (directly related to protobuf schema) IRClassProperty.ILPropertyOptions opts = new IRClassProperty.ILPropertyOptions(); // Add label to the property options. opts.Label = label; // Fetch the IR type of the property. - Doesn't actually matter, the Serialize handler will overwrite this. PropertyTypeKind propType = InspectorTools.DefaultTypeMapper(property, out refDefinition); // Construct IR reference placeholder. IRTypeNode irReference = null; if (propType == PropertyTypeKind.TYPE_REF) { irReference = InspectorTools.ConstructIRType(refDefinition); // And save the reference TYPEDEFINITION for the caller to process. references.Add(refDefinition); } // Fetch the fieldNumber for this property. var tag = ExtractFieldNumber(_subjectClass, property.Name); // Add it to the options. opts.PropertyOrder = tag; // Construct the IR property and store it. var prop = new IRClassProperty() { Name = property.Name, Type = propType, ReferencedType = irReference, Options = opts, }; properties.Add(prop); } return(properties); }
public static void SerializeOnCall(CallInfo info, List <string> property_names, List <FieldDefinition> allFields, List <IRClassProperty> properties, List <TypeDefinition> references) { if (!info.Method.Name.StartsWith("Write") || info.Method.Name.Equals("WriteUntil")) { // We are in no relevant method. return; } var fieldNameArg = info.Arguments[2].ToString(); var bracketIdx = fieldNameArg.LastIndexOf("["); var bracketLastIdx = fieldNameArg.LastIndexOf("]"); var fieldNameRef = UInt32.Parse(fieldNameArg.Substring(bracketIdx + 1, bracketLastIdx - bracketIdx - 1)); var fieldName = property_names[(int)fieldNameRef]; var type = info.Method.Name.Substring(5); var packedIdx = type.IndexOf("Packed"); var isPacked = false; if (packedIdx > -1) { Debug.Assert(packedIdx == 0); isPacked = true; type = type.Substring(6); } var specificType = InspectorTools.LiteralTypeMapper(type); var fieldIdx = (int)info.Arguments[1]; var label = FieldLabel.REQUIRED; if (info.Conditions.Any(c => c.Lhs.Contains("get_Count"))) { label = FieldLabel.REPEATED; } else if (info.Conditions.Any(c => c.Lhs.Contains("has"))) { label = FieldLabel.OPTIONAL; } // Construct IR reference placeholder. IRTypeNode irReference = null; if (specificType == PropertyTypeKind.TYPE_REF) { TypeReference fieldReference; if (info.Method.IsGenericInstance) { var method = info.Method as GenericInstanceMethod; fieldReference = method.GenericArguments[0]; } else { // Find out definition from arguments var cleanFieldName = fieldName.ToLower().Replace("_", ""); var fieldRef = allFields.OrderBy(f => f.Name.Length).First(field => field.Name.ToLower().Contains(cleanFieldName)); fieldReference = fieldRef.FieldType; } if (label == FieldLabel.REPEATED && fieldReference.IsGenericInstance) { var genericField = fieldReference as GenericInstanceType; fieldReference = genericField.GenericArguments[0]; } var fieldDefinition = fieldReference.Resolve(); irReference = InspectorTools.ConstructIRType(fieldDefinition); Debug.Assert(!fieldDefinition.FullName.Equals("System.UInt32")); // And save the reference TYPEDEFINITION for the caller to process. references.Add(fieldDefinition); } var newProperty = new IRClassProperty() { Name = fieldName, Type = specificType, ReferencedType = irReference, Options = new IRClassProperty.ILPropertyOptions() { Label = label, PropertyOrder = fieldIdx, IsPacked = isPacked, } }; properties.Add(newProperty); return; }
// A method was called by our inspected method. We use the collected information (our environment) // to extract information about the type (and fields). public static void SerializeOnCall(CallInfo info, List <byte> writtenBytes, List <IRClassProperty> properties) { if (info.Method.Name == "WriteByte") { int byteAmount = (int)info.Arguments[1]; writtenBytes.Add((byte)byteAmount); return; } if (info.Arguments.Any(x => x.ToString().Contains("GetSerializedSize()"))) { return; } if (!info.Method.Name.StartsWith("Write") && info.Method.Name != "Serialize") { return; } // !!! packed vs not packed: // bnet.protocol.channel_invitation.IncrementChannelCountResponse/reservation_tokens: *not* packed // PegasusGame.ChooseEntities/entities: *packed* // not packed = {{tag, data}, {tag, data}, ...} // packed = {tag, size, data} // repeated fixed fields are packed by default. // // not packed: // call: ProtocolParser.WriteUInt64(arg0, V_0) // conditions: arg1.get_ReservationTokens().get_Count() > 0, &V_1.MoveNext() == true // // packed: // call: ProtocolParser.WriteUInt32(arg0, V_0) // size // conditions: arg1.get_Entities().get_Count() > 0, &V_2.MoveNext() == false // call: ProtocolParser.WriteUInt64(arg0, V_3) // datum // conditions: arg1.get_Entities().get_Count() > 0, &V_2.MoveNext() == false, &V_4.MoveNext() == true var iterConds = info.Conditions.Where(x => x.Lhs.Contains("MoveNext")); var listConds = info.Conditions.Where(x => x.Lhs.Contains("().get_Count()")); if (listConds.Any() && !iterConds.Any(x => x.Cmp == Comparison.IsTrue)) { // Skip packed size writes: return; } // Get the packed flag. var packed = IsFieldPacked(iterConds); // Get the tag from the info. var tag = InspectorTools.GetFieldTag(writtenBytes); // Reset written bytes in order to process the next tag. writtenBytes.Clear(); // Get the field label. var label = GetFieldLabel(info, iterConds); // Get the name for the current tag. var name = GetFieldName(info, label); // Fetch the property object by it's name var prop = properties.First(x => x.Name == name); // In case the writing class is called BinaryWriter, the mapping is different. if (info.Method.DeclaringType.Name.Equals("BinaryWriter")) { prop.Type = FixedTypeMapper(prop); } // Set property options prop.Options.Label = label; prop.Options.PropertyOrder = tag; prop.Options.IsPacked = packed; // Default value is extracted from the deserialize method.. }