Skip to content

Commit

Permalink
calculate address size for serialization (#452)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jamiras authored Apr 1, 2024
1 parent bd8e866 commit 8a5ebbf
Show file tree
Hide file tree
Showing 43 changed files with 1,231 additions and 838 deletions.
8 changes: 4 additions & 4 deletions Source/Data/Leaderboard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,22 @@ public class Leaderboard : AssetBase
/// <summary>
/// Gets or sets the serialized start condition for the leaderboard.
/// </summary>
public string Start { get; set; }
public Trigger Start { get; set; }

/// <summary>
/// Gets or sets the serialized cancel condition for the leaderboard.
/// </summary>
public string Cancel { get; set; }
public Trigger Cancel { get; set; }

/// <summary>
/// Gets or sets the serialized submit condition for the leaderboard.
/// </summary>
public string Submit { get; set; }
public Trigger Submit { get; set; }

/// <summary>
/// Gets or sets the serialized value formula for the leaderboard.
/// </summary>
public string Value { get; set; }
public Value Value { get; set; }

/// <summary>
/// Gets or sets the format to use when displaying the value.
Expand Down
14 changes: 14 additions & 0 deletions Source/Data/RequirementGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,19 @@ public SoftwareVersion MinimumVersion()

return minimumVersion;
}

public uint MaximumAddress()
{
uint maximumAddress = 0;
foreach (var requirement in Requirements)
{
if (requirement.Left.IsMemoryReference)
maximumAddress = Math.Max(maximumAddress, requirement.Left.Value);
if (requirement.Right.IsMemoryReference)
maximumAddress = Math.Max(maximumAddress, requirement.Right.Value);
}

return maximumAddress;
}
}
}
6 changes: 5 additions & 1 deletion Source/Data/SerializationContext.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Jamiras.Components;
using System;
using System.Text;

namespace RATools.Data
{
Expand Down Expand Up @@ -46,5 +45,10 @@ public string FormatAddress(uint address)
return String.Format("{0:x6}", address);
}
}

public override string ToString()
{
return FormatAddress(0) + " (" + MinimumVersion + ")";
}
}
}
9 changes: 9 additions & 0 deletions Source/Data/Trigger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,5 +146,14 @@ public SoftwareVersion MinimumVersion()

return minimumVersion;
}

public uint MaximumAddress()
{
var maximumAddress = Core.MaximumAddress();
foreach (var alt in Alts)
maximumAddress = Math.Max(maximumAddress, alt.MaximumAddress());

return maximumAddress;
}
}
}
133 changes: 112 additions & 21 deletions Source/Data/Value.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,28 @@ private static Requirement DeserializeLegacyRequirement(Tokenizer tokenizer)
requirement.Left = Field.Deserialize(tokenizer);

requirement.Operator = Requirement.ReadOperator(tokenizer);
if (requirement.Operator != RequirementOperator.None &&
!requirement.IsComparison)
if (requirement.Operator == RequirementOperator.None)
{
if (requirement.Left.Type == FieldType.Value &&
(requirement.Left.Value & 0x80000000) != 0)
{
requirement.Type = RequirementType.SubSource;
requirement.Left = new Field
{
Type = FieldType.Value,
Value = (uint)(-((int)requirement.Left.Value))
};
}
}
else
{
requirement.Right = Field.Deserialize(tokenizer);
if (requirement.Right.Type == FieldType.Value &&

if (requirement.IsComparison)
{
requirement.Operator = RequirementOperator.None;
}
else if (requirement.Right.Type == FieldType.Value &&
(requirement.Right.Value & 0x80000000) != 0)
{
requirement.Type = RequirementType.SubSource;
Expand Down Expand Up @@ -185,6 +202,8 @@ public string Serialize(SerializationContext serializationContext)
{
if (serializationContext.MinimumVersion < Version._0_77) // Measured leaderboard format
SerializeLegacyRequirements(enumerator.Current.Requirements, builder, serializationContext);
else if (enumerator.Current.Requirements.Any(r => r.Type == RequirementType.None)) // raw value
SerializeLegacyRequirements(enumerator.Current.Requirements, builder, serializationContext);
else
enumerator.Current.Serialize(builder, serializationContext);

Expand All @@ -203,10 +222,17 @@ private static void SerializeLegacyRequirements(IEnumerable<Requirement> require
var enumerator = requirements.GetEnumerator();
if (enumerator.MoveNext())
{
bool first = true;
int constant = 0;
do
{
if (enumerator.Current.Left.IsMemoryReference)
{
if (first)
first = false;
else
builder.Append('_');

enumerator.Current.Left.Serialize(builder, serializationContext);

double multiplier = 1.0;
Expand All @@ -230,29 +256,55 @@ private static void SerializeLegacyRequirements(IEnumerable<Requirement> require

if (multiplier != 1.0)
{
if (enumerator.Current.Operator == RequirementOperator.Divide)
if (multiplier == Math.Floor(multiplier) && multiplier <= 0xFFFFFFFF && multiplier >= -0x80000000)
{
builder.Append('/');
builder.Append(1.0 / multiplier);
int scalar = (multiplier > 0x7FFFFFFF) ?
(int)(uint)multiplier : (int)multiplier;

builder.Append('*');
builder.Append(scalar);
}
else
{
builder.Append('*');
builder.Append(multiplier);
if (enumerator.Current.Operator == RequirementOperator.Divide && multiplier < 1.0f)
{
builder.Append('/');
multiplier = 1.0 / multiplier;
}
else
{
builder.Append('*');
}
builder.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0:0.0#####}", multiplier);

while (builder[builder.Length - 1] == '0')
builder.Length--;
if (builder[builder.Length - 1] == '.')
builder.Length--;
}
}
}
else
{
builder.Append('v');
builder.Append(enumerator.Current.Left.Value);
if (enumerator.Current.Type == RequirementType.SubSource)
constant -= (int)enumerator.Current.Left.Value;
else
constant += (int)enumerator.Current.Left.Value;
}
} while (enumerator.MoveNext());

if (!enumerator.MoveNext())
break;

builder.Append('_');
} while (true);
if (constant != 0 || first)
{
if (!first)
builder.Append('_');
builder.Append('v');
if (constant < 0)
{
constant = -constant;
builder.Append('-');
}
builder.Append(constant);
}
}
}

Expand All @@ -264,22 +316,61 @@ public SoftwareVersion MinimumVersion()
{
foreach (var requirement in value.Requirements)
{
Requirement clone = null;

if (requirement.Operator == RequirementOperator.Multiply ||
requirement.Operator == RequirementOperator.Divide)
{
// Multiply/Divide in trigger logic requires 0.78, but can be used in leaderboard values long before that.
var clone = requirement.Clone();
clone.Operator = RequirementOperator.None;
minimumVersion = minimumVersion.OrNewer(clone.MinimumVersion());
// Multiply/Divide in trigger logic requires 0.78, but can be used with constants
// in legacy value expressions long before that.
if (!requirement.Right.IsMemoryReference)
{
clone = requirement.Clone();
clone.Operator = RequirementOperator.None;

// float support in trigger logic requires 1.0
if (clone.Right.Type == FieldType.Float)
clone.Right = new Field { Type = FieldType.Value, Value = 1 };
}
}
else

switch (requirement.Type)
{
minimumVersion = minimumVersion.OrNewer(requirement.MinimumVersion());
case RequirementType.Measured:
// non-comparison Measured can be converted to legacy syntax
if (!requirement.IsComparison)
{
if (clone == null)
clone = requirement.Clone();
clone.Type = RequirementType.None;
}
break;

case RequirementType.AddHits:
case RequirementType.ResetIf:
case RequirementType.PauseIf:
// these are supported pre-0.77, but cannot be used in value logic without a Measured flag.
minimumVersion = minimumVersion.OrNewer(Version._0_77);
break;
}

if (clone != null)
minimumVersion = minimumVersion.OrNewer(clone.MinimumVersion());
else
minimumVersion = minimumVersion.OrNewer(requirement.MinimumVersion());
}
}

return minimumVersion;
}

public uint MaximumAddress()
{
uint maximumAddress = 0;
foreach (var value in Values)
maximumAddress = Math.Max(maximumAddress, value.MaximumAddress());

return maximumAddress;
}
}
}
5 changes: 0 additions & 5 deletions Source/Data/Version.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
using Jamiras.Components;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace RATools.Data
{
Expand Down
5 changes: 5 additions & 0 deletions Source/Parser/AchievementBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ public static SoftwareVersion GetMinimumVersion(Achievement achievement)
return minimumVersion;
}

public static uint GetMaximumAddress(Achievement achievement)
{
return achievement.Trigger.MaximumAddress();
}

/// <summary>
/// Creates a serialized requirements string from the core and alt groups of a provided <see cref="Achievement"/>.
/// </summary>
Expand Down
21 changes: 19 additions & 2 deletions Source/Parser/AchievementScriptInterpreter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -347,23 +347,40 @@ public bool Run(ExpressionGroupCollection expressionGroups, IScriptInterpreterCa
}

SoftwareVersion minimumVersion = scriptContext.SerializationContext.MinimumVersion;
uint maxAddress = 0;

foreach (var achievement in _achievements.Keys)
{
var achievementMinimumVersion = AchievementBuilder.GetMinimumVersion(achievement);
minimumVersion = minimumVersion.OrNewer(achievementMinimumVersion);

var achievementMaxAddress = AchievementBuilder.GetMaximumAddress(achievement);
maxAddress = Math.Max(maxAddress, achievementMaxAddress);
}

foreach (var leaderboard in _leaderboards.Keys)
{
var leaderboardMinimumVersion = LeaderboardBuilder.GetMinimumVersion(leaderboard);
minimumVersion = minimumVersion.OrNewer(leaderboardMinimumVersion);

var leaderboardMaxAddress = LeaderboardBuilder.GetMaximumAddress(leaderboard);
maxAddress = Math.Max(maxAddress, leaderboardMaxAddress);
}

minimumVersion = minimumVersion.OrNewer(RichPresenceBuilder.MinimumVersion());
maxAddress = Math.Max(maxAddress, RichPresenceBuilder.MaximumAddress());

SerializationContext = scriptContext.SerializationContext.WithVersion(minimumVersion);
minimumVersion = minimumVersion.OrNewer(RichPresenceBuilder.MinimumVersion());

if (!String.IsNullOrEmpty(_richPresence.DisplayString))
SerializationContext = scriptContext.SerializationContext.WithVersion(minimumVersion);
if (maxAddress >= 0x10000)
SerializationContext.AddressWidth = 6;
else if (maxAddress >= 0x100)
SerializationContext.AddressWidth = 4;
else
SerializationContext.AddressWidth = 2;

if (_richPresence.IsValid)
{
RichPresence = _richPresence.Serialize(SerializationContext);
RichPresenceLine = _richPresence.Line;
Expand Down
2 changes: 1 addition & 1 deletion Source/Parser/Expressions/Trigger/FieldFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ internal static Field ApplyMathematic(Field left, RequirementOperator operation,
return new Field();
}

return new Field { Type = FieldType.Float, Size = FieldSize.DWord, Float = floatResult };
return new Field { Type = FieldType.Float, Size = FieldSize.Float, Float = floatResult };

default:
return new Field();
Expand Down
Loading

0 comments on commit 8a5ebbf

Please sign in to comment.