public void TestVersions() { Assert.That(UnityHeaders.GetTypeHeaderForVersion("5.4.3p4").VersionRange.ToString(), Is.EqualTo("5.4.1 - 5.4.3")); Assert.That(UnityHeaders.GetTypeHeaderForVersion("5.6.4").VersionRange.ToString(), Is.EqualTo("5.6.1 - 5.6.7")); Assert.That(new UnityVersion("2020.1.0b5").ToString(), Is.EqualTo("2020.1.0b5")); Assert.That(new UnityVersion("2020.1").ToString(), Is.EqualTo("2020.1.0")); Assert.That(new UnityVersion("5.3.1").CompareTo("5.3.1p4") == 0); Assert.That(new UnityVersion("5.3.1rc0").CompareTo("5.3.1p2") < 0); Assert.That(new UnityVersion("5.3.1f1").CompareTo("5.3.1p0") < 0); }
/// <summary> /// User has selected an image /// </summary> private void LstImages_OnSelectionChanged(object sender, SelectionChangedEventArgs e) { // Selection has been removed? if (((ListBox)sender).SelectedItem == null) { trvNamespaces.ItemsSource = null; return; } // Get selected image var model = (AppModel)((ListBox)sender).SelectedItem; // Get namespaces var namespaces = model.ILModel.Assemblies.SelectMany(x => x.DefinedTypes).GroupBy(t => t.Namespace).Select(n => n.Key); // Break namespaces down into a tree var namespaceTree = deconstructNamespaces(namespaces); // Uncheck the default exclusions foreach (var exclusion in Constants.DefaultExcludedNamespaces) { var parts = exclusion.Split('.'); CheckboxNode node = null; foreach (var part in parts) { node = (node?.Children ?? namespaceTree).FirstOrDefault(c => c.Name == part); if (node == null) { break; } } if (node != null) { node.IsChecked = false; } } // Populate TreeView with namespace hierarchy trvNamespaces.ItemsSource = namespaceTree; // Populate Unity version combo boxes var prevIdaSelection = cboPyUnityVersion.SelectedItem; var prevCppSelection = cboCppUnityVersion.SelectedItem; var prevJsonSelection = cboJsonUnityVersion.SelectedItem; cboPyUnityVersion.Items.Clear(); cboCppUnityVersion.Items.Clear(); cboJsonUnityVersion.Items.Clear(); foreach (var version in UnityHeaders.GuessHeadersForBinary(model.Package.Binary)) { cboPyUnityVersion.Items.Add(version); cboCppUnityVersion.Items.Add(version); cboJsonUnityVersion.Items.Add(version); } // Restore previous selection via value equality if (prevIdaSelection != null) { cboPyUnityVersion.SelectedItem = cboPyUnityVersion.Items.Cast <UnityHeaders>().FirstOrDefault(v => v.Equals(prevIdaSelection)); cboCppUnityVersion.SelectedItem = cboCppUnityVersion.Items.Cast <UnityHeaders>().FirstOrDefault(v => v.Equals(prevCppSelection)); cboJsonUnityVersion.SelectedItem = cboJsonUnityVersion.Items.Cast <UnityHeaders>().FirstOrDefault(v => v.Equals(prevJsonSelection)); } // Prefer latest Unity versions if there was no previous selection or it's now invalid if (cboPyUnityVersion.SelectedItem == null) { cboPyUnityVersion.SelectedIndex = cboPyUnityVersion.Items.Count - 1; } if (cboCppUnityVersion.SelectedItem == null) { cboCppUnityVersion.SelectedIndex = cboCppUnityVersion.Items.Count - 1; } if (cboJsonUnityVersion.SelectedItem == null) { cboJsonUnityVersion.SelectedIndex = cboJsonUnityVersion.Items.Count - 1; } }
public void TestCppTypeDeclarations() { // NOTE: This test doesn't check for correct results, only that parsing doesn't fail! var unityTypeHeaders = UnityHeaders.GetAllTypeHeaders(); // Ensure we have read the embedded assembly resources Assert.IsTrue(unityTypeHeaders.Any()); // Ensure we can interpret every header from every version of Unity without errors // This will throw InvalidOperationException if there is a problem foreach (var unityTypeHeader in unityTypeHeaders) { var cppUnityHeaderTypes = new CppTypeCollection(64); cppUnityHeaderTypes.AddFromDeclarationText(unityTypeHeader.GetText()); foreach (var cppType in cppUnityHeaderTypes.Types) { Debug.WriteLine("// " + cppType.Key + "\n" + cppType.Value.ToString("o")); } } // Do a few sanity checks taken from real applications (64-bit) // NOTE: Does not provide full code coverage! var cppTypes = CppTypeCollection.FromUnityVersion(new UnityVersion("2019.3.1f1")); CppComplexType ct; CppField field; // Field offset tests // Un-nested class ct = (CppComplexType)cppTypes["Il2CppClass"]; field = ct[0xE0].First(); Assert.AreEqual("cctor_finished", field.Name); field = ct[0x130].First(); Assert.AreEqual("vtable", field.Name); field = ct["cctor_finished"]; Assert.AreEqual(0xE0, field.OffsetBytes); field = ct["vtable"]; Assert.AreEqual(0x130, field.OffsetBytes); // Nested class ct = (CppComplexType)cppTypes["Il2CppClass_Merged"]; var fields = ct.Flattened; field = fields[0xE0].First(); Assert.AreEqual("cctor_finished", field.Name); field = fields[0x130].First(); Assert.AreEqual("vtable", field.Name); field = fields["cctor_finished"]; Assert.AreEqual(0xE0, field.OffsetBytes); field = fields["vtable"]; Assert.AreEqual(0x130, field.OffsetBytes); // Bitfield ct = (CppComplexType)cppTypes["Il2CppType"]; field = ct.Fields[0xB * 8 + 7].First(); Assert.AreEqual("pinned", field.Name); // Nested fields ct = (CppComplexType)cppTypes["Il2CppWin32Decimal"]; fields = ct.Flattened; field = fields[0x08].First(); Assert.AreEqual("lo32", field.Name); field = fields[0x08].Last(); Assert.AreEqual("lo64", field.Name); field = fields[0x0C].First(); Assert.AreEqual("mid32", field.Name); // Pointer alias var alias = (CppAlias)cppTypes.GetType("Il2CppHString"); Assert.AreEqual(typeof(CppPointerType), alias.ElementType.GetType()); Assert.AreEqual("Il2CppHString__ *", alias.ElementType.Name); // Typedef struct with no tag Assert.True(cppTypes.Types.ContainsKey("Il2CppGenericMethodIndices")); Assert.True(((CppComplexType)cppTypes["Il2CppGenericMethodIndices"]).ComplexValueType == ComplexValueType.Struct); // Alignment tests // Il2CppArrayType has an int * (sizes) after three uint8_t Assert.AreEqual(0x10, cppTypes.GetComplexType("Il2CppArrayType")["sizes"].OffsetBytes); // Il2CppSafeArray has a uint32_t (element_size) after two uint16_t Assert.AreEqual(0x4, cppTypes.GetComplexType("Il2CppSafeArray")["element_size"].OffsetBytes); }