diff --git a/Source/Data/CodeNote.cs b/Source/Data/CodeNote.cs
index 86997390..4fb36ef8 100644
--- a/Source/Data/CodeNote.cs
+++ b/Source/Data/CodeNote.cs
@@ -86,9 +86,14 @@ private void SetNote(string note)
if (Summary.IndexOfAny(new[] { '=', ':' }) != -1)
ExtractValuesFromSummary();
+
+ // if we found a size, stop looking for a size/pointer
+ if (Size != FieldSize.None)
+ break;
}
- if (line.Length >= 4) // nBit is smallest parsable note
+ if (line.Length >= 4 && // nBit is smallest parsable note
+ Size == FieldSize.None)
{
var lower = line.ToString().ToLower();
if (!lower.Contains("pointer"))
@@ -108,7 +113,7 @@ private void SetNote(string note)
}
// if we found a size, stop looking for a size/pointer
- if (Size != FieldSize.None)
+ if (Size != FieldSize.None && !String.IsNullOrEmpty(Summary))
break;
}
} while (tokenizer.NextChar != '\0');
@@ -659,6 +664,12 @@ private static bool IsValue(Token token)
private void CheckValue(Token clause)
{
+ int prefixIndex = 0;
+ while (prefixIndex < clause.Length && !Char.IsLetterOrDigit(clause[prefixIndex]) && clause[prefixIndex] != '[')
+ prefixIndex++;
+ if (prefixIndex > 0)
+ clause = clause.SubToken(prefixIndex);
+
var separatorLength = 1;
var separator = clause.IndexOf('=');
@@ -673,6 +684,27 @@ private void CheckValue(Token clause)
separatorLength = 2;
}
+ if (separator == -1)
+ {
+ // only match dash if no other separators were found. it could be definine a range
+ var dash = clause.IndexOf(" - ");
+ if (dash != -1)
+ {
+ separator = dash;
+ separatorLength = 3;
+ }
+ }
+
+ if (separator == -1 && clause.Length > 4 && clause[0] == '[')
+ {
+ var endMeta = clause.IndexOf(']');
+ if (endMeta != -1)
+ {
+ clause = clause.SubToken(1);
+ separator = endMeta - 1;
+ }
+ }
+
if (separator != -1)
{
var left = clause.SubToken(0, separator).Trim();
diff --git a/Source/Parser/AchievementScriptInterpreter.cs b/Source/Parser/AchievementScriptInterpreter.cs
index 05b8cdba..9d97b274 100644
--- a/Source/Parser/AchievementScriptInterpreter.cs
+++ b/Source/Parser/AchievementScriptInterpreter.cs
@@ -197,6 +197,11 @@ internal static InterpreterScope GetGlobalScope()
}
private static InterpreterScope _globalScope;
+ public static bool IsReservedFunctionName(string functionName)
+ {
+ return (GetGlobalScope().GetFunction(functionName) != null);
+ }
+
///
/// Gets the error message generated by the script if processing failed.
///
diff --git a/Source/Parser/PublishedAssets.cs b/Source/Parser/PublishedAssets.cs
index 4dfb04cf..bb01e689 100644
--- a/Source/Parser/PublishedAssets.cs
+++ b/Source/Parser/PublishedAssets.cs
@@ -42,6 +42,8 @@ public PublishedAssets(string filename, IFileSystemService fileSystemService)
public int GameId { get; private set; }
+ public int ConsoleId { get; private set; }
+
///
/// Gets the title of the associated game.
///
@@ -96,6 +98,7 @@ private void Read()
var publishedData = new JsonObject(stream);
Title = publishedData.GetField("Title").StringValue;
+ ConsoleId = publishedData.GetField("ConsoleID").IntegerValue ?? 0;
var sets = publishedData.GetField("Sets");
if (sets.Type == JsonFieldType.ObjectArray)
diff --git a/Source/Parser/ScriptBuilderContext.cs b/Source/Parser/ScriptBuilderContext.cs
index 226a042d..8e3ec5f0 100644
--- a/Source/Parser/ScriptBuilderContext.cs
+++ b/Source/Parser/ScriptBuilderContext.cs
@@ -13,7 +13,7 @@ public ScriptBuilderContext()
NumberFormat = NumberFormat.Decimal;
WrapWidth = Int32.MaxValue;
Indent = 0;
- _aliases = new Dictionary();
+ _aliases = new List>();
}
public NumberFormat NumberFormat { get; set; }
@@ -47,11 +47,31 @@ public ScriptBuilderContext Clone()
private Requirement _lastAndNext;
private int _remainingWidth;
- private Dictionary _aliases;
+ private List> _aliases;
public void AddAlias(string memoryReference, string alias)
{
- _aliases[memoryReference] = alias;
+ for (int i = 0; i < _aliases.Count; i++)
+ {
+ if (_aliases[i].Key == memoryReference)
+ {
+ _aliases[i] = new KeyValuePair(memoryReference, alias);
+ return;
+ }
+ }
+
+ _aliases.Add(new KeyValuePair(memoryReference, alias));
+ }
+
+ public string GetAliasDefinition(string alias)
+ {
+ foreach (var kvp in _aliases)
+ {
+ if (kvp.Value == alias)
+ return kvp.Key;
+ }
+
+ return null;
}
public override string ToString()
diff --git a/Source/ViewModels/GameViewModel.cs b/Source/ViewModels/GameViewModel.cs
index 62b627f9..001cccc9 100644
--- a/Source/ViewModels/GameViewModel.cs
+++ b/Source/ViewModels/GameViewModel.cs
@@ -77,6 +77,7 @@ private class NavigationItem
private bool _disableNavigationCapture = false;
internal int GameId { get; private set; }
+ internal int ConsoleId { get; private set; }
internal string RACacheDirectory { get; private set; }
internal Dictionary Notes { get; private set; }
internal SerializationContext SerializationContext { get; set; }
@@ -666,6 +667,7 @@ private void ReadPublished()
_publishedSets.AddRange(publishedAssets.Sets);
_publishedRichPresence = publishedAssets.RichPresence;
Title = publishedAssets.Title;
+ ConsoleId = publishedAssets.ConsoleId;
_logger.WriteVerbose(String.Format("Identified {0} core achievements ({1} points)", coreCount, corePoints));
_logger.WriteVerbose(String.Format("Identified {0} unofficial achievements ({1} points)", unofficialCount, unofficialPoints));
diff --git a/Source/ViewModels/NewScriptDialogViewModel.cs b/Source/ViewModels/NewScriptDialogViewModel.cs
index f5790352..cee78775 100644
--- a/Source/ViewModels/NewScriptDialogViewModel.cs
+++ b/Source/ViewModels/NewScriptDialogViewModel.cs
@@ -213,25 +213,8 @@ private void AddMemoryReferences(AssetSourceViewModel asset, DumpAsset dumpAsset
{
foreach (var group in trigger.Groups)
{
- foreach (var requirement in group.Requirements)
- {
- if (requirement.Requirement == null)
- continue;
-
- if (requirement.Requirement.Left.IsMemoryReference)
- {
- var memoryItem = AddMemoryAddress(requirement.Requirement.Left);
- if (memoryItem != null && !dumpAsset.MemoryAddresses.Contains(memoryItem))
- dumpAsset.MemoryAddresses.Add(memoryItem);
- }
-
- if (requirement.Requirement.Right.IsMemoryReference)
- {
- var memoryItem = AddMemoryAddress(requirement.Requirement.Right);
- if (memoryItem != null && !dumpAsset.MemoryAddresses.Contains(memoryItem))
- dumpAsset.MemoryAddresses.Add(memoryItem);
- }
- }
+ AddMemoryReferences(dumpAsset,
+ group.Requirements.Where(r => r.Requirement != null).Select(r => r.Requirement));
}
}
}
@@ -310,7 +293,8 @@ private void LoadNotes()
size = FieldSize.Byte;
break;
}
- AddMemoryAddress(new Field { Size = size, Type = FieldType.MemoryAddress, Value = note.Address });
+
+ AddMemoryAddress(new Field { Size = size, Type = FieldType.MemoryAddress, Value = note.Address }, null);
}
}
@@ -416,12 +400,10 @@ private void LoadRichPresence(string richPresenceFile)
var achievement = new AchievementBuilder();
achievement.ParseRequirements(Tokenizer.CreateTokenizer(trigger));
- foreach (var requirement in achievement.CoreRequirements)
- AddMemoryReferences(dumpRichPresence, requirement);
+ AddMemoryReferences(dumpRichPresence, achievement.CoreRequirements);
foreach (var alt in achievement.AlternateRequirements)
- foreach (var requirement in alt)
- AddMemoryReferences(dumpRichPresence, requirement);
+ AddMemoryReferences(dumpRichPresence, alt);
}
AddMacroMemoryReferences(dumpRichPresence, line.Substring(index + 1));
@@ -468,12 +450,10 @@ private void AddMacroMemoryReferences(DumpAsset displayRichPresence, string disp
var achievement = new AchievementBuilder();
achievement.ParseRequirements(Tokenizer.CreateTokenizer(parameter));
- foreach (var requirement in achievement.CoreRequirements)
- AddMemoryReferences(macro, requirement);
+ AddMemoryReferences(macro, achievement.CoreRequirements);
foreach (var alt in achievement.AlternateRequirements)
- foreach (var requirement in alt)
- AddMemoryReferences(macro, requirement);
+ AddMemoryReferences(macro, alt);
}
else
{
@@ -484,7 +464,7 @@ private void AddMacroMemoryReferences(DumpAsset displayRichPresence, string disp
var field = Field.Deserialize(Tokenizer.CreateTokenizer(operand));
if (field.IsMemoryReference)
{
- var memoryItem = AddMemoryAddress(field);
+ var memoryItem = AddMemoryAddress(field, null);
if (memoryItem != null && !macro.MemoryAddresses.Contains(memoryItem))
macro.MemoryAddresses.Add(memoryItem);
}
@@ -526,47 +506,96 @@ private void MergeOpenTickets()
}
}
- private void AddMemoryReferences(DumpAsset dumpAsset, Requirement requirement)
+ private void AddMemoryReferences(DumpAsset dumpAsset, IEnumerable requirements)
{
- if (requirement.Left.IsMemoryReference)
+ MemoryItem parent = null;
+ foreach (var requirement in requirements)
{
- var memoryItem = AddMemoryAddress(requirement.Left);
- if (memoryItem != null && !dumpAsset.MemoryAddresses.Contains(memoryItem))
- dumpAsset.MemoryAddresses.Add(memoryItem);
- }
+ MemoryItem leftMemoryItem = null;
+ if (requirement.Left.IsMemoryReference)
+ leftMemoryItem = AddMemoryAddress(requirement.Left, parent);
- if (requirement.Right.IsMemoryReference)
- {
- var memoryItem = AddMemoryAddress(requirement.Right);
- if (memoryItem != null && !dumpAsset.MemoryAddresses.Contains(memoryItem))
- dumpAsset.MemoryAddresses.Add(memoryItem);
+ MemoryItem rightMemoryItem = null;
+ if (requirement.Right.IsMemoryReference)
+ rightMemoryItem = AddMemoryAddress(requirement.Right, parent);
+
+ if (leftMemoryItem == null && rightMemoryItem == null)
+ {
+ if (parent != null && (requirement.Left.IsMemoryReference || requirement.Right.IsMemoryReference))
+ {
+ // offset not found in parent, capture the parent
+ if (!dumpAsset.MemoryAddresses.Contains(parent))
+ dumpAsset.MemoryAddresses.Add(parent);
+ }
+
+ if (requirement.Type == RequirementType.AddAddress)
+ {
+ // provide a dummy parent for the next condition
+ parent = new MemoryItem(uint.MaxValue, FieldSize.None, null);
+ continue;
+ }
+ }
+
+ if (requirement.Type == RequirementType.AddAddress)
+ {
+ // only want to capture the leaves
+ parent = leftMemoryItem ?? rightMemoryItem;
+ }
+ else
+ {
+ parent = null;
+ }
+
+ if (leftMemoryItem != null)
+ {
+ leftMemoryItem.IsReferenced = true;
+ if (!dumpAsset.MemoryAddresses.Contains(leftMemoryItem))
+ dumpAsset.MemoryAddresses.Add(leftMemoryItem);
+ }
+
+ if (rightMemoryItem != null)
+ {
+ rightMemoryItem.IsReferenced = true;
+ if (!dumpAsset.MemoryAddresses.Contains(rightMemoryItem))
+ dumpAsset.MemoryAddresses.Add(rightMemoryItem);
+ }
}
}
- private MemoryItem AddMemoryAddress(Field field)
+ private MemoryItem AddMemoryAddress(Field field, MemoryItem parent)
{
+ var items = parent?.ChainedItems ?? _memoryItems;
+
int index = 0;
- while (index < _memoryItems.Count)
+ while (index < items.Count)
{
- if (_memoryItems[index].Address > field.Value)
+ if (items[index].Address > field.Value)
break;
- if (_memoryItems[index].Address == field.Value)
+ if (items[index].Address == field.Value)
{
- if (_memoryItems[index].Size > field.Size)
+ if (items[index].Size > field.Size)
break;
- if (_memoryItems[index].Size == field.Size)
- return _memoryItems[index];
+ if (items[index].Size == field.Size)
+ return items[index];
}
index++;
}
CodeNote note;
- if (!_game.Notes.TryGetValue(field.Value, out note))
+ if (parent != null)
+ {
+ note = parent.Note?.OffsetNotes.FirstOrDefault(n => n.Address == field.Value);
+ if (note == null)
+ return null;
+ }
+ else if (!_game.Notes.TryGetValue(field.Value, out note))
+ {
return null;
+ }
- var item = new MemoryItem(field.Value, field.Size, note.Summary);
- _memoryItems.Insert(index, item);
+ var item = new MemoryItem(field.Value, field.Size, note) { Parent = parent };
+ items.Insert(index, item);
return item;
}
@@ -882,11 +911,7 @@ private void UpdateMemoryGrid()
}
if (functionNameStyle != FunctionNameStyle.None)
- {
- CodeNote note;
- if (_game.Notes.TryGetValue(memoryItem.Address, out note))
- memoryItem.UpdateFunctionName(functionNameStyle, note);
- }
+ memoryItem.UpdateFunctionName(functionNameStyle);
if (rowItem == null || rowItem.Address != memoryItem.Address || rowItem.Size != memoryItem.Size)
MemoryAddresses.InsertRow(memIndex, memoryItem);
@@ -905,23 +930,22 @@ private void UpdateFunctionNames()
foreach (var row in MemoryAddresses.Rows)
{
var memoryItem = (MemoryItem)row.Model;
-
- CodeNote note;
- if (_game.Notes.TryGetValue(memoryItem.Address, out note))
- memoryItem.UpdateFunctionName(functionNameStyle, note);
+ memoryItem.UpdateFunctionName(functionNameStyle);
}
}
[DebuggerDisplay("{Size} {Address}")]
public class MemoryItem : ViewModelBase
{
- public MemoryItem(uint address, FieldSize size, string notes)
+ public MemoryItem(uint address, FieldSize size, CodeNote note)
{
Address = address;
Size = size;
- Notes = notes;
+ Note = note;
}
+ private CodeNote _note;
+
public static readonly ModelProperty AddressProperty = ModelProperty.Register(typeof(MemoryItem), "Address", typeof(uint), (uint)0);
public uint Address
@@ -947,22 +971,53 @@ public string FunctionName
public static readonly ModelProperty NotesProperty = ModelProperty.Register(typeof(MemoryItem), "Notes", typeof(string), String.Empty);
+ public CodeNote Note
+ {
+ get { return _note; }
+ private set
+ {
+ _note = value;
+ Notes = value?.Summary ?? string.Empty;
+ }
+ }
+
public string Notes
{
get { return (string)GetValue(NotesProperty); }
private set { SetValue(NotesProperty, value); }
}
- public void UpdateFunctionName(FunctionNameStyle style, CodeNote note)
+ public bool HasChainedItems
+ {
+ get { return _chainedItems != null; }
+ }
+
+ public List ChainedItems
+ {
+ get
+ {
+ if (_chainedItems == null)
+ _chainedItems = new List();
+
+ return _chainedItems;
+ }
+ }
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private List _chainedItems;
+
+ public MemoryItem Parent { get; set; }
+ public bool IsReferenced { get; set; }
+
+ public void UpdateFunctionName(FunctionNameStyle style)
{
- if (style == FunctionNameStyle.None)
+ if (style == FunctionNameStyle.None || _note == null)
{
FunctionName = String.Empty;
return;
}
- var text = note.Summary;
- var subNote = note.GetSubNote(Size);
+ var text = _note.Summary;
+ var subNote = _note.GetSubNote(Size);
if (subNote != null)
text += ' ' + subNote;
@@ -1005,7 +1060,14 @@ public void UpdateFunctionName(FunctionNameStyle style, CodeNote note)
// build the function name
var functionName = BuildVariableName(text, style);
if (!String.IsNullOrEmpty(functionName))
+ {
+ functionName = functionName.Replace("'s ", "s ");
+
+ if (AchievementScriptInterpreter.IsReservedFunctionName(functionName))
+ functionName += '_';
+
FunctionName = functionName.ToString();
+ }
}
}
@@ -1035,6 +1097,67 @@ private static string EscapeString(string input)
return input.Replace("\"", "\\\"");
}
+ private uint GetMask()
+ {
+ var mask = 0xFFFFFFFF;
+
+ switch (_game.ConsoleId)
+ {
+ case 40: // Dreamcast
+ case 78: // DSi
+ case 2: // N64
+ case 12: // PlayStation
+ mask = 0x00FFFFFF;
+ break;
+
+ case 16: // GameCube
+ // GameCube docs suggest masking with 0x1FFFFFFF (extra F).
+ // both work. check to see which the game is using.
+ mask = 0x01FFFFFF;
+ foreach (var achievement in _game.Editors.OfType())
+ {
+ foreach (var trigger in achievement.Published.TriggerList)
+ {
+ foreach (var group in trigger.Groups)
+ {
+ foreach (var requirement in group.Requirements)
+ {
+ if (requirement.Requirement.Type == RequirementType.AddAddress &&
+ requirement.Requirement.Operator == RequirementOperator.BitwiseAnd &&
+ requirement.Requirement.Right.Type == FieldType.Value)
+ {
+ if (requirement.Requirement.Right.Value >= mask)
+ return requirement.Requirement.Right.Value;
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case 21: // PlayStation2
+ case 41: // PSP
+ mask = 0x01FFFFFF;
+ break;
+
+ case 19: // WII
+ mask = 0x1FFFFFFF;
+ break;
+
+ case 5: // GBA
+ // This is technically wrong as there's two distinct maps required.
+ // $03000000 -> $00000000 (via just doing a 24-bit read)
+ // $02000000 -> $00008000 (via an offset)
+ // However, most developers who have implemented sets just do a 24-bit
+ // read _and_ use a +0x8000 offset when necessary. As these offsets are
+ // encoded in the code notes, we shouldn't provide an explicit offset here.
+ mask = 0x00FFFFFF;
+ break;
+ }
+
+ return mask;
+ }
+
internal void Dump(Stream outStream)
{
MemoryAddresses.Commit();
@@ -1046,12 +1169,11 @@ internal void Dump(Stream outStream)
NumberFormat = _settings.HexValues ? NumberFormat.Hexadecimal : NumberFormat.Decimal,
};
- foreach (var memoryItem in _memoryItems.Where(m => !String.IsNullOrEmpty(m.FunctionName)))
- {
- var memoryReference = Field.GetMemoryReference(memoryItem.Address, memoryItem.Size);
- var functionCall = memoryItem.FunctionName + "()";
- scriptBuilderContext.AddAlias(memoryReference, functionCall);
- }
+ var mask = GetMask();
+
+ AddAliases(scriptBuilderContext, _memoryItems, mask);
+ foreach (var asset in _assets)
+ AddAliases(scriptBuilderContext, asset.MemoryAddresses, mask);
using (var stream = new StreamWriter(outStream))
{
@@ -1065,7 +1187,7 @@ internal void Dump(Stream outStream)
var lookupsToDump = _assets.Where(a => a.Type == DumpAssetType.Lookup && a.IsSelected).ToList();
- DumpMemoryAccessors(stream, lookupsToDump);
+ DumpMemoryAccessors(stream, lookupsToDump, scriptBuilderContext);
foreach (var dumpLookup in lookupsToDump)
DumpLookup(stream, dumpLookup);
@@ -1081,6 +1203,122 @@ internal void Dump(Stream outStream)
}
}
+ private void AddAliases(ScriptBuilderContext scriptBuilderContext, IEnumerable items, uint mask)
+ {
+ foreach (var memoryItem in items.Where(m => !String.IsNullOrEmpty(m.FunctionName)))
+ {
+ var functionCall = memoryItem.FunctionName + "()";
+ string memoryReference;
+
+ if (memoryItem.Parent == null)
+ {
+ memoryReference = Field.GetMemoryReference(memoryItem.Address, memoryItem.Size);
+ }
+ else
+ {
+ var context = scriptBuilderContext.Clone();
+ var requirements = new List();
+ for (var parent = memoryItem.Parent; parent != null; parent = parent.Parent)
+ {
+ var requirement = new Requirement
+ {
+ Type = RequirementType.AddAddress,
+ Left = new Field { Type = FieldType.MemoryAddress, Size = parent.Size, Value = parent.Address },
+ };
+
+ if (mask != 0xFFFFFFFF)
+ {
+ if (mask == 0x00FFFFFF)
+ {
+ if (Field.GetByteSize(requirement.Left.Size) > 3)
+ requirement.Left = requirement.Left.ChangeSize(FieldSize.TByte);
+ }
+ else
+ {
+ requirement.Operator = RequirementOperator.BitwiseAnd;
+ requirement.Right = new Field { Type = FieldType.Value, Size = FieldSize.None, Value = mask };
+ }
+ }
+
+ requirements.Add(requirement);
+ }
+
+ requirements.Reverse();
+
+ requirements.Add(new Requirement
+ {
+ Left = new Field { Type = FieldType.MemoryAddress, Size = memoryItem.Size, Value = memoryItem.Address }
+ });
+
+ var builder = new StringBuilder();
+ context.AppendRequirements(builder, requirements);
+ memoryReference = builder.ToString();
+ }
+
+ if (memoryReference != functionCall)
+ {
+ var existing = scriptBuilderContext.GetAliasDefinition(functionCall);
+ if (existing != null && existing != memoryReference)
+ {
+ var updateItem = memoryItem;
+
+ string suffixedFunctionName = memoryItem.FunctionName + "_";
+ if (memoryItem.Size == memoryItem.Note.Size)
+ {
+ // this size matches the note size, get it the unsuffixed alias
+ scriptBuilderContext.AddAlias(memoryReference, functionCall);
+
+ memoryReference = existing;
+ suffixedFunctionName += existing.Substring(0, existing.IndexOf('('));
+
+ updateItem = FindAlternateMemoryItemByFunctionName(_memoryItems, memoryItem);
+ }
+ else
+ {
+ suffixedFunctionName += Field.GetSizeFunction(memoryItem.Size);
+ }
+
+ var count = 1;
+
+ do
+ {
+ updateItem.FunctionName = (count == 1) ? suffixedFunctionName : (suffixedFunctionName + count);
+ functionCall = updateItem.FunctionName + "()";
+
+ existing = scriptBuilderContext.GetAliasDefinition(functionCall);
+ if (existing == null || existing == memoryReference)
+ break;
+
+ count++;
+ } while (true);
+ }
+
+ scriptBuilderContext.AddAlias(memoryReference, functionCall);
+ }
+ }
+ }
+
+ private static MemoryItem FindAlternateMemoryItemByFunctionName(List memoryItems, MemoryItem memoryItem)
+ {
+ foreach (var scan in memoryItems)
+ {
+ if (scan.FunctionName == memoryItem.FunctionName && !ReferenceEquals(scan, memoryItem))
+ return scan;
+ }
+
+ foreach (var scan in memoryItems)
+ {
+ if (scan.HasChainedItems)
+ {
+ var child = FindAlternateMemoryItemByFunctionName(scan.ChainedItems, memoryItem);
+ if (child != null)
+ return child;
+ }
+ }
+
+ return null;
+ }
+
private readonly Dictionary _achievementSetVariables;
private void DumpSets(StreamWriter stream, IEnumerable publishedSets)
@@ -1121,7 +1359,7 @@ private void DumpSets(StreamWriter stream, IEnumerable published
}
}
- private void DumpMemoryAccessors(StreamWriter stream, List lookupsToDump)
+ private void DumpMemoryAccessors(StreamWriter stream, List lookupsToDump, ScriptBuilderContext scriptBuilderContext)
{
string addressFormat = "{0:X4}";
if (_memoryItems.Count > 0 && _memoryItems[_memoryItems.Count - 1].Address > 0xFFFF)
@@ -1135,16 +1373,9 @@ private void DumpMemoryAccessors(StreamWriter stream, List lookupsToD
uint previousNoteAddress = UInt32.MaxValue;
foreach (var memoryItem in _memoryItems)
{
- if (filter == CodeNoteFilter.ForSelectedAssets)
- {
- if (MemoryAddresses.GetRow(memoryItem) == null)
- continue;
- }
-
- CodeNote note = null;
if (dumpNotes != NoteDump.None)
{
- if (_game.Notes.TryGetValue(memoryItem.Address, out note))
+ if (memoryItem.Note != null)
{
if (String.IsNullOrEmpty(memoryItem.FunctionName))
{
@@ -1156,11 +1387,11 @@ private void DumpMemoryAccessors(StreamWriter stream, List lookupsToD
}
}
- if (note != null && memoryItem.Address != previousNoteAddress)
+ if (memoryItem.Note != null && memoryItem.Address != previousNoteAddress)
{
previousNoteAddress = memoryItem.Address;
- var notes = note.Note.Trim();
+ var notes = memoryItem.Note.Note.Trim();
if (notes.Length > 0)
{
if (needLine || hadFunction || !String.IsNullOrEmpty(memoryItem.FunctionName))
@@ -1188,37 +1419,82 @@ private void DumpMemoryAccessors(StreamWriter stream, List lookupsToD
}
}
+ if (filter == CodeNoteFilter.ForSelectedAssets)
+ {
+ if (MemoryAddresses.GetRow(memoryItem) == null)
+ continue;
+ }
+
if (!String.IsNullOrEmpty(memoryItem.FunctionName))
{
- if (needLine)
- {
- needLine = false;
- stream.WriteLine();
- }
+ DumpMemoryFunction(stream, lookupsToDump, scriptBuilderContext, memoryItem, ref needLine);
hadFunction = true;
+ }
+ else
+ {
+ hadFunction = false;
+ }
- if (memoryItem.FunctionName.EndsWith("()"))
- memoryItem.FunctionName = memoryItem.FunctionName.Substring(0, memoryItem.FunctionName.Length - 2);
+ if (memoryItem.HasChainedItems)
+ hadFunction |= DumpNestedMemoryFunctions(stream, lookupsToDump, scriptBuilderContext, memoryItem, ref needLine);
+ }
+ }
- stream.Write("function ");
- stream.Write(memoryItem.FunctionName);
- stream.Write("() => ");
- var memoryReference = Field.GetMemoryReference(memoryItem.Address, memoryItem.Size);
- stream.WriteLine(memoryReference);
+ private bool DumpNestedMemoryFunctions(StreamWriter stream, List lookupsToDump, ScriptBuilderContext scriptBuilderContext, MemoryItem parent, ref bool needLine)
+ {
+ bool hadFunction = false;
- foreach (var dumpLookup in lookupsToDump)
- {
- if (dumpLookup.MemoryAddresses.Count == 1 && dumpLookup.MemoryAddresses[0].Address == memoryItem.Address && dumpLookup.MemoryAddresses[0].Size == memoryItem.Size)
- {
- DumpLookup(stream, dumpLookup);
- lookupsToDump.Remove(dumpLookup);
- break;
- }
- }
+ foreach (var memoryItem in parent.ChainedItems)
+ {
+ if (memoryItem.IsReferenced && !String.IsNullOrEmpty(memoryItem.FunctionName))
+ {
+ DumpMemoryFunction(stream, lookupsToDump, scriptBuilderContext, memoryItem, ref needLine);
+ hadFunction = true;
}
- else
+
+ if (memoryItem.HasChainedItems)
+ hadFunction |= DumpNestedMemoryFunctions(stream, lookupsToDump, scriptBuilderContext, memoryItem, ref needLine);
+ }
+
+ return hadFunction;
+ }
+
+ private void DumpMemoryFunction(StreamWriter stream, List lookupsToDump, ScriptBuilderContext scriptBuilderContext, MemoryItem memoryItem, ref bool needLine)
+ {
+ string memoryReference;
+
+ if (memoryItem.Parent == null)
+ {
+ memoryReference = Field.GetMemoryReference(memoryItem.Address, memoryItem.Size);
+ }
+ else
+ {
+ memoryReference = scriptBuilderContext.GetAliasDefinition(memoryItem.FunctionName + "()");
+ if (memoryReference == null)
+ return;
+ }
+
+ if (needLine)
+ {
+ needLine = false;
+ stream.WriteLine();
+ }
+
+ if (memoryItem.FunctionName.EndsWith("()"))
+ memoryItem.FunctionName = memoryItem.FunctionName.Substring(0, memoryItem.FunctionName.Length - 2);
+
+ stream.Write("function ");
+ stream.Write(memoryItem.FunctionName);
+ stream.Write("() => ");
+ stream.WriteLine(memoryReference);
+
+ foreach (var dumpLookup in lookupsToDump)
+ {
+ if (dumpLookup.MemoryAddresses.Count == 1 && dumpLookup.MemoryAddresses[0].Address == memoryItem.Address && dumpLookup.MemoryAddresses[0].Size == memoryItem.Size)
{
- hadFunction = false;
+ DumpLookup(stream, dumpLookup);
+ lookupsToDump.Remove(dumpLookup);
+ break;
}
}
}
@@ -1786,13 +2062,6 @@ private static void DumpPublishedRequirements(StreamWriter stream, DumpAsset dum
context.IsValue = isValue;
context.AppendRequirements(definition, requirementGroupViewModel.Requirements.Select(r => r.Requirement));
- foreach (var memoryItem in dumpAsset.MemoryAddresses.Where(m => !String.IsNullOrEmpty(m.FunctionName)))
- {
- var memoryReference = Field.GetMemoryReference(memoryItem.Address, memoryItem.Size);
- var functionCall = memoryItem.FunctionName + "()";
- definition.Replace(memoryReference, functionCall);
- }
-
stream.Write(definition.ToString());
}
}
diff --git a/Tests/Regression/DumpTests.cs b/Tests/Regression/DumpTests.cs
index a3214847..3a401eec 100644
--- a/Tests/Regression/DumpTests.cs
+++ b/Tests/Regression/DumpTests.cs
@@ -83,7 +83,7 @@ public void DumpTest(string patchDataFileName)
vmNewScript.SelectedCodeNotesFilter = CodeNoteFilter.ForSelectedAssets;
vmNewScript.SelectedFunctionNameStyle = FunctionNameStyle.SnakeCase;
- vmNewScript.SelectedNoteDump = NoteDump.All;
+ vmNewScript.SelectedNoteDump = NoteDump.OnlyForDefinedMethods;
vmNewScript.CheckAllCommand.Execute();
var expectedFileName = Path.Combine(baseDir, vmNewScript.GameId.Value + ".rascript");