Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

calculate address size for serialization #452

Merged
merged 15 commits into from
Apr 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading