diff --git a/InfectedRose.Utilities/Checksum.cs b/InfectedRose.Utilities/Checksum.cs index 07ec082..f5b8959 100644 --- a/InfectedRose.Utilities/Checksum.cs +++ b/InfectedRose.Utilities/Checksum.cs @@ -8,29 +8,6 @@ namespace InfectedRose.Utilities { public static class Checksum { - private struct ChecksumLayer - { - internal uint Id { get; set; } - - internal uint Layer { get; set; } - - internal uint Revision { get; set; } - - public void Apply(ref uint value, ref uint total) - { - foreach (var reference in new[] {Id, Layer, Revision}) - { - value += reference >> 16; // Apply reference - - total += value; // Add to total - - value += reference & ushort.MaxValue; // Make ushort - - total += value; // Add to total - } - } - } - /// /// Calculate the checksum for a LEGO Universe zone /// @@ -126,6 +103,10 @@ public static uint Generate(string zone) var lower = (ushort) ((value & ushort.MaxValue) + (value >> 16)); var upper = (ushort) ((total & ushort.MaxValue) + (total >> 16)); + // + // The checksum has two parts, one for the 'total', and one for the 'value', these combine to form a 32bit value + // + return (uint) (upper << 16 | lower); } diff --git a/InfectedRose.Utilities/ChecksumLayer.cs b/InfectedRose.Utilities/ChecksumLayer.cs new file mode 100644 index 0000000..934e143 --- /dev/null +++ b/InfectedRose.Utilities/ChecksumLayer.cs @@ -0,0 +1,25 @@ +namespace InfectedRose.Utilities +{ + internal struct ChecksumLayer + { + internal uint Id { get; set; } + + internal uint Layer { get; set; } + + internal uint Revision { get; set; } + + internal void Apply(ref uint value, ref uint total) + { + foreach (var reference in new[] {Id, Layer, Revision}) + { + value += reference >> 16; // Apply reference + + total += value; // Add to total + + value += reference & ushort.MaxValue; // Make ushort + + total += value; // Add to total + } + } + } +} \ No newline at end of file diff --git a/InfectedRose.Utilities/Extensions/LuzFileExtensions.cs b/InfectedRose.Utilities/Extensions/LuzFileExtensions.cs new file mode 100644 index 0000000..49f63e4 --- /dev/null +++ b/InfectedRose.Utilities/Extensions/LuzFileExtensions.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using InfectedRose.Luz; +using InfectedRose.Lvl; + +namespace InfectedRose.Utilities +{ + public static class LuzFileExtensions + { + public static uint GenerateChecksum(this LuzFile @this, List scenes) + { + if (@this.Scenes.Length != scenes.Count) + { + throw new ArgumentOutOfRangeException(nameof(scenes), "The count of scenes has to equal the count of scenes in this luz file."); + } + + uint value = ushort.MaxValue; // For checksum calculations + uint total = ushort.MaxValue; // Sum of all changes applied to value + + var zoneLayer = new ChecksumLayer + { + Id = uint.MaxValue, + Layer = default, + Revision = @this.RevisionNumber + }; + + zoneLayer.Apply(ref value, ref total); + + for (var index = 0; index < scenes.Count; index++) + { + var scene = scenes[index]; + var lvl = @this.Scenes[index]; + + // + // Get revision + // + + var revision = scene.LevelInfo?.RevisionNumber ?? scene.OldLevelHeader.Revision; + + // + // Get layer + // + + var sceneLayer = new ChecksumLayer + { + Id = lvl.SceneId, + Layer = lvl.LayerId, + Revision = revision + }; + + sceneLayer.Apply(ref value, ref total); + } + + // + // Get final checksum + // + + var lower = (ushort) ((value & ushort.MaxValue) + (value >> 16)); + var upper = (ushort) ((total & ushort.MaxValue) + (total >> 16)); + + // + // The checksum has two parts, one for the 'total', and one for the 'value', these combine to form a 32bit value + // + + return (uint) (upper << 16 | lower); + } + } +} \ No newline at end of file diff --git a/InfectedRose.Utilities/InfectedRose.Utilities.csproj b/InfectedRose.Utilities/InfectedRose.Utilities.csproj index ca5cc9b..808ca48 100644 --- a/InfectedRose.Utilities/InfectedRose.Utilities.csproj +++ b/InfectedRose.Utilities/InfectedRose.Utilities.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.1