diff --git a/Source/Data/CodeNote.cs b/Source/Data/CodeNote.cs index 80c133e2..1cc55ed5 100644 --- a/Source/Data/CodeNote.cs +++ b/Source/Data/CodeNote.cs @@ -894,5 +894,14 @@ private static bool GetBitRange(Token token, out char lowBit, out char highBit) return false; } + + public static CodeNote ResolveNote(uint address, IDictionary notes, CodeNote parentNote) + { + if (parentNote != null) + return parentNote.OffsetNotes.FirstOrDefault(n => n.Address == address); + + notes.TryGetValue(address, out CodeNote note); + return note; + } } } diff --git a/Source/ViewModels/RequirementComparisonViewModel.cs b/Source/ViewModels/RequirementComparisonViewModel.cs index 897fa6d9..4e6a4a16 100644 --- a/Source/ViewModels/RequirementComparisonViewModel.cs +++ b/Source/ViewModels/RequirementComparisonViewModel.cs @@ -7,8 +7,8 @@ namespace RATools.ViewModels { public class RequirementComparisonViewModel : RequirementViewModel { - public RequirementComparisonViewModel(Requirement requirement, Requirement compareRequirement, NumberFormat numberFormat, IDictionary notes) - : base(requirement, numberFormat, notes) + public RequirementComparisonViewModel(Requirement requirement, Requirement compareRequirement, NumberFormat numberFormat, IDictionary notes, CodeNote parentNote) + : base(requirement, numberFormat, notes, parentNote) { if (compareRequirement == null) { diff --git a/Source/ViewModels/RequirementGroupViewModel.cs b/Source/ViewModels/RequirementGroupViewModel.cs index cd833907..92ced86f 100644 --- a/Source/ViewModels/RequirementGroupViewModel.cs +++ b/Source/ViewModels/RequirementGroupViewModel.cs @@ -20,8 +20,12 @@ public RequirementGroupViewModel(string label, Label = label; var list = new List(); + CodeNote parentNote = null; foreach (var requirement in requirements) - list.Add(new RequirementViewModel(requirement, numberFormat, notes)); + { + list.Add(new RequirementViewModel(requirement, numberFormat, notes, parentNote)); + parentNote = UpdateParentNote(requirement, parentNote, notes); + } UpdateDependencies(list); Requirements = list; @@ -200,22 +204,44 @@ private static void UpdateDependencies(List list) } } + private static CodeNote UpdateParentNote(Requirement requirement, CodeNote parentNote, IDictionary notes) + { + if (requirement.Type == RequirementType.AddAddress && requirement.Left.IsMemoryReference) + { + var leftNote = CodeNote.ResolveNote(requirement.Left.Value, notes, parentNote); + return (leftNote != null && leftNote.IsPointer) ? leftNote : null; + } + else if (requirement.Type != RequirementType.AddAddress) + { + return null; + } + return parentNote; + } + private static void AppendRequirements(List list, RequirementEx left, RequirementEx right, NumberFormat numberFormat, IDictionary notes) { if (right == null) { + CodeNote parentNote = null; foreach (var requirement in left.Requirements) - list.Add(new RequirementComparisonViewModel(requirement, null, numberFormat, notes)); + { + list.Add(new RequirementComparisonViewModel(requirement, null, numberFormat, notes, parentNote)); + parentNote = UpdateParentNote(requirement, parentNote, notes); + } } else if (left == null) { foreach (var requirement in right.Requirements) - list.Add(new RequirementComparisonViewModel(null, requirement, numberFormat, notes)); + list.Add(new RequirementComparisonViewModel(null, requirement, numberFormat, notes, null)); } else if (left.Requirements.Count == right.Requirements.Count) { + CodeNote parentNote = null; for (int i = 0; i < left.Requirements.Count; ++i) - list.Add(new RequirementComparisonViewModel(left.Requirements[i], right.Requirements[i], numberFormat, notes)); + { + list.Add(new RequirementComparisonViewModel(left.Requirements[i], right.Requirements[i], numberFormat, notes, parentNote)); + parentNote = UpdateParentNote(left.Requirements[i], parentNote, notes); + } } else { @@ -272,6 +298,7 @@ private static void AppendRequirements(List list, Requirem } var rightIndex = 0; + CodeNote parentNote = null; foreach (var requirement in left.Requirements) { @@ -283,17 +310,18 @@ private static void AppendRequirements(List list, Requirem { var rightRequirement = right.Requirements[rightIndex++]; if (unmatchedCompareRequirements.Remove(rightRequirement)) - list.Add(new RequirementComparisonViewModel(null, rightRequirement, numberFormat, notes)); + list.Add(new RequirementComparisonViewModel(null, rightRequirement, numberFormat, notes, null)); } rightIndex++; } - list.Add(new RequirementComparisonViewModel(requirement, match, numberFormat, notes)); + list.Add(new RequirementComparisonViewModel(requirement, match, numberFormat, notes, parentNote)); + parentNote = UpdateParentNote(requirement, parentNote, notes); } foreach (var requirement in unmatchedCompareRequirements) - list.Add(new RequirementComparisonViewModel(null, requirement, numberFormat, notes)); + list.Add(new RequirementComparisonViewModel(null, requirement, numberFormat, notes, null)); } } @@ -450,7 +478,7 @@ public RequirementGroupViewModel(string label, IEnumerable requirem while (GetBestMerge(list, out leftIndex, out rightIndex)) { list[leftIndex] = new RequirementComparisonViewModel(list[leftIndex].Requirement, - ((RequirementComparisonViewModel)list[rightIndex]).CompareRequirement, numberFormat, notes); + ((RequirementComparisonViewModel)list[rightIndex]).CompareRequirement, numberFormat, notes, null); list.RemoveAt(rightIndex); } diff --git a/Source/ViewModels/RequirementViewModel.cs b/Source/ViewModels/RequirementViewModel.cs index 0421919f..4f4a261c 100644 --- a/Source/ViewModels/RequirementViewModel.cs +++ b/Source/ViewModels/RequirementViewModel.cs @@ -14,7 +14,7 @@ namespace RATools.ViewModels [DebuggerDisplay("{Requirement}")] public class RequirementViewModel : ViewModelBase { - public RequirementViewModel(Requirement requirement, NumberFormat numberFormat, IDictionary notes) + public RequirementViewModel(Requirement requirement, NumberFormat numberFormat, IDictionary notes, CodeNote parentNote) { Requirement = requirement; @@ -28,14 +28,15 @@ public RequirementViewModel(Requirement requirement, NumberFormat numberFormat, { var builder = new StringBuilder(); - CodeNote note; - if (notes.TryGetValue(requirement.Left.Value, out note)) + var note = CodeNote.ResolveNote(requirement.Left.Value, notes, parentNote); + if (note != null) { var subNote = note.GetSubNote(requirement.Left.Size); builder.AppendFormat("0x{0:x6}:{1}", requirement.Left.Value, subNote ?? note.Summary); } - if (notes.TryGetValue(requirement.Right.Value, out note)) + note = CodeNote.ResolveNote(requirement.Right.Value, notes, parentNote); + if (note != null) { if (builder.Length > 0) builder.AppendLine(); @@ -48,8 +49,8 @@ public RequirementViewModel(Requirement requirement, NumberFormat numberFormat, } else { - CodeNote note; - if (notes.TryGetValue(requirement.Left.Value, out note)) + var note = CodeNote.ResolveNote(requirement.Left.Value, notes, parentNote); + if (note != null) { Notes = note.Note; var subNote = note.GetSubNote(requirement.Left.Size); @@ -63,8 +64,8 @@ public RequirementViewModel(Requirement requirement, NumberFormat numberFormat, } else if (requirement.Right.IsMemoryReference) { - CodeNote note; - if (notes.TryGetValue(requirement.Right.Value, out note)) + var note = CodeNote.ResolveNote(requirement.Right.Value, notes, parentNote); + if (note != null) { Notes = note.Note; var subNote = note.GetSubNote(requirement.Left.Size); diff --git a/Tests/ViewModels/RequirementComparisonViewModelTests.cs b/Tests/ViewModels/RequirementComparisonViewModelTests.cs index db293c87..f799fa18 100644 --- a/Tests/ViewModels/RequirementComparisonViewModelTests.cs +++ b/Tests/ViewModels/RequirementComparisonViewModelTests.cs @@ -36,7 +36,7 @@ public void TestDefinitions(string leftSerialized, string rightSerialized, builder.ParseRequirements(Tokenizer.CreateTokenizer(rightSerialized)); var compareRequirement = builder.ToAchievement().CoreRequirements.First(); - var vmRequirement = new RequirementComparisonViewModel(requirement, compareRequirement, NumberFormat.Decimal, notes); + var vmRequirement = new RequirementComparisonViewModel(requirement, compareRequirement, NumberFormat.Decimal, notes, null); Assert.That(vmRequirement.Definition, Is.EqualTo(expectedDefinition)); Assert.That(vmRequirement.OtherDefinition, Is.EqualTo(expectedOtherDefinition)); @@ -52,7 +52,7 @@ public void TestAddedRequirement() builder.ParseRequirements(Tokenizer.CreateTokenizer("0xH1234=7")); var requirement = builder.ToAchievement().CoreRequirements.First(); - var vmRequirement = new RequirementComparisonViewModel(requirement, null, NumberFormat.Decimal, notes); + var vmRequirement = new RequirementComparisonViewModel(requirement, null, NumberFormat.Decimal, notes, null); Assert.That(vmRequirement.Definition, Is.EqualTo("byte(0x001234) == 7")); Assert.That(vmRequirement.OtherDefinition, Is.EqualTo("")); @@ -68,7 +68,7 @@ public void TestRemovedRequirement() builder.ParseRequirements(Tokenizer.CreateTokenizer("0xH1234=7")); var requirement = builder.ToAchievement().CoreRequirements.First(); - var vmRequirement = new RequirementComparisonViewModel(null, requirement, NumberFormat.Decimal, notes); + var vmRequirement = new RequirementComparisonViewModel(null, requirement, NumberFormat.Decimal, notes, null); Assert.That(vmRequirement.Definition, Is.EqualTo("")); Assert.That(vmRequirement.OtherDefinition, Is.EqualTo("byte(0x001234) == 7")); diff --git a/Tests/ViewModels/RequirementGroupViewModelTests.cs b/Tests/ViewModels/RequirementGroupViewModelTests.cs index 45ecaa03..ffe02d07 100644 --- a/Tests/ViewModels/RequirementGroupViewModelTests.cs +++ b/Tests/ViewModels/RequirementGroupViewModelTests.cs @@ -1,4 +1,4 @@ -using Jamiras.Components; +using Jamiras.Components; using Moq; using NUnit.Framework; using RATools.Data; @@ -6,6 +6,7 @@ using RATools.Services; using RATools.ViewModels; using System.Collections.Generic; +using System.Linq; using System.Text; namespace RATools.Tests.ViewModels @@ -106,5 +107,37 @@ public void TestDisplay(string serialized, string expected) Assert.That(strBuilder.ToString(), Is.EqualTo(expected)); } + + [Test] + [TestCase("I:0xX1000_I:0xX0068_0xH0004=1", // nested pointer + new[] { "[32-bit pointer] root\n+0x68 | [32-bit pointer] nested\n++0x4 | [8-bit] value" }, + new[] { "[32-bit pointer] root\n+0x68 | [32-bit pointer] nested\n++0x4 | [8-bit] value", "[32-bit pointer] nested\n+0x4 | [8-bit] value", "[8-bit] value" })] + [TestCase("I:0xX1000_0xH0004=1_0xH2000=2", // resets after pointer chain + new[] { "[32-bit pointer] root\n+0x4 | [8-bit] hp", "unrelated" }, + new[] { "[32-bit pointer] root\n+0x4 | [8-bit] hp", "[8-bit] hp", "unrelated" })] + public void TestNotes(string serialized, string[] notes, string[] expectedNotes) + { + var mockSettings = new Mock(); + mockSettings.Setup(s => s.HexValues).Returns(false); + ServiceRepository.Reset(); + ServiceRepository.Instance.RegisterInstance(mockSettings.Object); + + var builder = new AchievementBuilder(); + builder.ParseRequirements(Tokenizer.CreateTokenizer(serialized)); + var requirements = builder.ToAchievement().CoreRequirements; + + // notes are for 0x1000, 0x2000, ... + var notesDict = new Dictionary(); + for (int i = 0; i < notes.Length; i++) + { + uint address = (uint)(0x1000 * (i + 1)); + notesDict[address] = new CodeNote(address, notes[i]); + } + + var vmRequirementGroup = new RequirementGroupViewModel("Group", requirements, NumberFormat.Decimal, notesDict); + + for (int i = 0; i < expectedNotes.Length; i++) + Assert.That(vmRequirementGroup.Requirements.ElementAt(i).Notes, Is.EqualTo(expectedNotes[i])); + } } } diff --git a/Tests/ViewModels/RequirementViewModelTests.cs b/Tests/ViewModels/RequirementViewModelTests.cs index 8f5ac0ae..78fdb57d 100644 --- a/Tests/ViewModels/RequirementViewModelTests.cs +++ b/Tests/ViewModels/RequirementViewModelTests.cs @@ -1,4 +1,4 @@ -using Jamiras.Components; +using Jamiras.Components; using Moq; using NUnit.Framework; using RATools.Data; @@ -33,7 +33,7 @@ public void TestDefinition(string serialized, string expected) var requirement = builder.ToAchievement().CoreRequirements.First(); var notes = new Dictionary(); - var vmRequirement = new RequirementViewModel(requirement, NumberFormat.Decimal, notes); + var vmRequirement = new RequirementViewModel(requirement, NumberFormat.Decimal, notes, null); Assert.That(vmRequirement.Definition, Is.EqualTo(expected)); } @@ -64,7 +64,7 @@ public void TestNotes(string serialized, string expectedNote, string expectedSho var builder = new AchievementBuilder(); builder.ParseRequirements(Tokenizer.CreateTokenizer(serialized)); var requirement = builder.ToAchievement().CoreRequirements.First(); - var vmRequirement = new RequirementViewModel(requirement, NumberFormat.Decimal, notes); + var vmRequirement = new RequirementViewModel(requirement, NumberFormat.Decimal, notes, null); Assert.That(vmRequirement.Notes, Is.EqualTo(expectedNote)); @@ -92,7 +92,7 @@ public void TestNoteShortening(string serialized, string expected) var builder = new AchievementBuilder(); builder.ParseRequirements(Tokenizer.CreateTokenizer(serialized)); var requirement = builder.ToAchievement().CoreRequirements.First(); - var vmRequirement = new RequirementViewModel(requirement, NumberFormat.Decimal, notes); + var vmRequirement = new RequirementViewModel(requirement, NumberFormat.Decimal, notes, null); Assert.That(vmRequirement.NotesShort, Is.EqualTo(expected)); @@ -118,7 +118,7 @@ public void TestAddSourceTrailingConstantComparison() }; var notes = new Dictionary(); - var vmRequirement = new RequirementViewModel(requirement, NumberFormat.Decimal, notes); + var vmRequirement = new RequirementViewModel(requirement, NumberFormat.Decimal, notes, null); Assert.That(vmRequirement.Definition, Is.EqualTo("always_false()")); @@ -145,7 +145,7 @@ public void TestAddSourceTrailingConstantComparisonWithHits() }; var notes = new Dictionary(); - var vmRequirement = new RequirementViewModel(requirement, NumberFormat.Decimal, notes); + var vmRequirement = new RequirementViewModel(requirement, NumberFormat.Decimal, notes, null); Assert.That(vmRequirement.Definition, Is.EqualTo("repeated(10, always_false())"));