diff --git a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj
index 6727a86a91..3bec56d322 100644
--- a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj
+++ b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj
@@ -1,6 +1,6 @@
-
+
Debug
AnyCPU
@@ -22,7 +22,6 @@
DEBUG;TRACE
prompt
4
- 6
AnyCPU
@@ -102,9 +101,6 @@
-
- osu.licenseheader
-
PreserveNewest
diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index 91c0da6f65..e4e9807754 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -1,5 +1,6 @@
-
+
+
{419659FD-72EA-4678-9EB8-B22A746CED70}
Debug
@@ -62,7 +63,6 @@
false
- 6
none
@@ -98,7 +98,6 @@
full
AnyCPU
false
- 6
prompt
--tests
@@ -174,9 +173,6 @@
-
- osu.licenseheader
-
diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs
index b2f7fdabfc..9901dbde18 100644
--- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs
+++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs
@@ -1,8 +1,14 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
+using System.Collections.Generic;
+using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
+using osu.Game.Rulesets.Catch.UI;
+using osu.Game.Rulesets.Objects.Types;
+using OpenTK;
namespace osu.Game.Rulesets.Catch.Beatmaps
{
@@ -18,6 +24,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
CatchHitObject lastObj = null;
+ initialiseHyperDash(beatmap.HitObjects);
+
foreach (var obj in beatmap.HitObjects)
{
if (obj.NewCombo)
@@ -34,5 +42,49 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
lastObj = obj;
}
}
+
+ private void initialiseHyperDash(List objects)
+ {
+ // todo: add difficulty adjust.
+ double halfCatcherWidth = CatcherArea.CATCHER_SIZE * (objects.FirstOrDefault()?.Scale ?? 1) / CatchPlayfield.BASE_WIDTH / 2;
+
+ int lastDirection = 0;
+ double lastExcess = halfCatcherWidth;
+
+ int objCount = objects.Count;
+
+ for (int i = 0; i < objCount - 1; i++)
+ {
+ CatchHitObject currentObject = objects[i];
+
+ // not needed?
+ // if (currentObject is TinyDroplet) continue;
+
+ CatchHitObject nextObject = objects[i + 1];
+
+ // while (nextObject is TinyDroplet)
+ // {
+ // if (++i == objCount - 1) break;
+ // nextObject = objects[i + 1];
+ // }
+
+ int thisDirection = nextObject.X > currentObject.X ? 1 : -1;
+ double timeToNext = nextObject.StartTime - ((currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime) - 4;
+ double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth);
+
+ if (timeToNext * CatcherArea.Catcher.BASE_SPEED < distanceToNext)
+ {
+ currentObject.HyperDashTarget = nextObject;
+ lastExcess = halfCatcherWidth;
+ }
+ else
+ {
+ //currentObject.DistanceToHyperDash = timeToNext - distanceToNext;
+ lastExcess = MathHelper.Clamp(timeToNext - distanceToNext, 0, halfCatcherWidth);
+ }
+
+ lastDirection = thisDirection;
+ }
+ }
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
index cb4e6453ce..38757d4928 100644
--- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
@@ -27,6 +27,16 @@ namespace osu.Game.Rulesets.Catch.Objects
public float Scale { get; set; } = 1;
+ ///
+ /// Whether this fruit can initiate a hyperdash.
+ ///
+ public bool HyperDash => HyperDashTarget != null;
+
+ ///
+ /// The target fruit if we are to initiate a hyperdash.
+ ///
+ public CatchHitObject HyperDashTarget;
+
public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
{
base.ApplyDefaults(controlPointInfo, difficulty);
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs
index 4c28a9d021..9f46bbd3a4 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs
@@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
using OpenTK;
+using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
@@ -70,6 +71,20 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
}
}
};
+
+ if (HitObject.HyperDash)
+ {
+ Add(new Pulp
+ {
+ RelativePositionAxes = Axes.Both,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ AccentColour = Color4.Red,
+ Blending = BlendingMode.Additive,
+ Alpha = 0.5f,
+ Scale = new Vector2(2)
+ });
+ }
}
}
}
diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs
index 538f6930ed..daa3e12800 100644
--- a/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs
+++ b/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs
@@ -17,6 +17,7 @@ namespace osu.Game.Rulesets.Catch.Tests
internal class TestCaseCatcherArea : OsuTestCase
{
private RulesetInfo catchRuleset;
+ private TestCatcherArea catcherArea;
public override IReadOnlyList RequiredTypes => new[]
{
@@ -26,6 +27,7 @@ namespace osu.Game.Rulesets.Catch.Tests
public TestCaseCatcherArea()
{
AddSliderStep("CircleSize", 0, 8, 5, createCatcher);
+ AddToggleStep("Hyperdash", t => catcherArea.ToggleHyperDash(t));
}
private void createCatcher(float size)
@@ -33,7 +35,7 @@ namespace osu.Game.Rulesets.Catch.Tests
Child = new CatchInputManager(catchRuleset)
{
RelativeSizeAxes = Axes.Both,
- Child = new CatcherArea(new BeatmapDifficulty { CircleSize = size })
+ Child = catcherArea = new TestCatcherArea(new BeatmapDifficulty { CircleSize = size })
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.BottomLeft
@@ -46,5 +48,15 @@ namespace osu.Game.Rulesets.Catch.Tests
{
catchRuleset = rulesets.GetRuleset(2);
}
+
+ private class TestCatcherArea : CatcherArea
+ {
+ public TestCatcherArea(BeatmapDifficulty beatmapDifficulty)
+ : base(beatmapDifficulty)
+ {
+ }
+
+ public void ToggleHyperDash(bool status) => MovableCatcher.HyperDashModifier = status ? 2 : 1;
+ }
}
}
diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs
new file mode 100644
index 0000000000..ce3f79bae2
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs
@@ -0,0 +1,30 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using NUnit.Framework;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Catch.Objects;
+
+namespace osu.Game.Rulesets.Catch.Tests
+{
+ [TestFixture]
+ [Ignore("getting CI working")]
+ public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer
+ {
+ public TestCaseHyperdash()
+ : base(typeof(CatchRuleset))
+ {
+ }
+
+ protected override Beatmap CreateBeatmap()
+ {
+ var beatmap = new Beatmap();
+
+ for (int i = 0; i < 512; i++)
+ if (i % 5 < 3)
+ beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = i * 100, NewCombo = i % 8 == 0 });
+
+ return beatmap;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
index 6fd0793500..76dbfa77c6 100644
--- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
@@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Catch.UI
{
public class CatchPlayfield : ScrollingPlayfield
{
- public static readonly float BASE_WIDTH = 512;
+ public const float BASE_WIDTH = 512;
protected override Container Content => content;
private readonly Container content;
@@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Catch.UI
};
}
- public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.CanCatch(obj);
+ public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.AttemptCatch(obj);
public override void Add(DrawableHitObject h)
{
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
index 203db1bb8c..2bb0f3cd18 100644
--- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
@@ -14,6 +14,7 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
+using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.UI
{
@@ -21,18 +22,18 @@ namespace osu.Game.Rulesets.Catch.UI
{
public const float CATCHER_SIZE = 172;
- private readonly Catcher catcher;
+ protected readonly Catcher MovableCatcher;
public Container ExplodingFruitTarget
{
- set { catcher.ExplodingFruitTarget = value; }
+ set { MovableCatcher.ExplodingFruitTarget = value; }
}
public CatcherArea(BeatmapDifficulty difficulty = null)
{
RelativeSizeAxes = Axes.X;
Height = CATCHER_SIZE;
- Child = catcher = new Catcher(difficulty)
+ Child = MovableCatcher = new Catcher(difficulty)
{
AdditiveTarget = this,
};
@@ -41,17 +42,17 @@ namespace osu.Game.Rulesets.Catch.UI
public void Add(DrawableHitObject fruit, Vector2 absolutePosition)
{
fruit.RelativePositionAxes = Axes.None;
- fruit.Position = new Vector2(catcher.ToLocalSpace(absolutePosition).X - catcher.DrawSize.X / 2, 0);
+ fruit.Position = new Vector2(MovableCatcher.ToLocalSpace(absolutePosition).X - MovableCatcher.DrawSize.X / 2, 0);
fruit.Anchor = Anchor.TopCentre;
fruit.Origin = Anchor.BottomCentre;
fruit.Scale *= 0.7f;
fruit.LifetimeEnd = double.MaxValue;
- catcher.Add(fruit);
+ MovableCatcher.Add(fruit);
}
- public bool CanCatch(CatchHitObject obj) => Math.Abs(catcher.Position.X - obj.X) < catcher.DrawSize.X * Math.Abs(catcher.Scale.X) / DrawSize.X / 2;
+ public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj);
public class Catcher : Container, IKeyBindingHandler
{
@@ -105,14 +106,35 @@ namespace osu.Game.Rulesets.Catch.UI
dashing = value;
- if (dashing)
- Schedule(addAdditiveSprite);
+ Trail |= dashing;
}
}
- private void addAdditiveSprite()
+ private bool trail;
+
+ ///
+ /// Activate or deactive the trail. Will be automatically deactivated when conditions to keep the trail displayed are no longer met.
+ ///
+ protected bool Trail
{
- if (!dashing || AdditiveTarget == null) return;
+ get { return trail; }
+ set
+ {
+ if (value == trail) return;
+
+ trail = value;
+
+ if (Trail)
+ beginTrail();
+ }
+ }
+
+ private void beginTrail()
+ {
+ Trail &= dashing || HyperDashing;
+ Trail &= AdditiveTarget != null;
+
+ if (!Trail) return;
var additive = createCatcherSprite();
@@ -120,6 +142,7 @@ namespace osu.Game.Rulesets.Catch.UI
additive.OriginPosition = additive.OriginPosition + new Vector2(DrawWidth / 2, 0); // also temporary to align sprite correctly.
additive.Position = Position;
additive.Scale = Scale;
+ additive.Colour = HyperDashing ? Color4.Red : Color4.White;
additive.RelativePositionAxes = RelativePositionAxes;
additive.Blending = BlendingMode.Additive;
@@ -127,7 +150,7 @@ namespace osu.Game.Rulesets.Catch.UI
additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire();
- Scheduler.AddDelayed(addAdditiveSprite, 50);
+ Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50);
}
private Sprite createCatcherSprite() => new Sprite
@@ -138,6 +161,10 @@ namespace osu.Game.Rulesets.Catch.UI
OriginPosition = new Vector2(-3, 10) // temporary until the sprite is aligned correctly.
};
+ ///
+ /// Add a caught fruit to the catcher's stack.
+ ///
+ /// The fruit that was caught.
public void Add(DrawableHitObject fruit)
{
float distance = fruit.DrawSize.X / 2 * fruit.Scale.X;
@@ -150,10 +177,80 @@ namespace osu.Game.Rulesets.Catch.UI
caughtFruit.Add(fruit);
- if (((CatchHitObject)fruit.HitObject).LastInCombo)
+ var catchObject = (CatchHitObject)fruit.HitObject;
+
+ if (catchObject.LastInCombo)
explode();
}
+ ///
+ /// Let the catcher attempt to catch a fruit.
+ ///
+ /// The fruit to catch.
+ /// Whether the catch is possible.
+ public bool AttemptCatch(CatchHitObject fruit)
+ {
+ const double relative_catcher_width = CATCHER_SIZE / 2;
+
+ // this stuff wil disappear once we move fruit to non-relative coordinate space in the future.
+ var catchObjectPosition = fruit.X * CatchPlayfield.BASE_WIDTH;
+ var catcherPosition = Position.X * CatchPlayfield.BASE_WIDTH;
+
+ var validCatch =
+ catchObjectPosition >= catcherPosition - relative_catcher_width / 2 &&
+ catchObjectPosition <= catcherPosition + relative_catcher_width / 2;
+
+ if (validCatch && fruit.HyperDash)
+ {
+ HyperDashModifier = Math.Abs(fruit.HyperDashTarget.X - fruit.X) / Math.Abs(fruit.HyperDashTarget.StartTime - fruit.StartTime) / BASE_SPEED;
+ HyperDashDirection = fruit.HyperDashTarget.X - fruit.X;
+ }
+ else
+ HyperDashModifier = 1;
+
+ return validCatch;
+ }
+
+ ///
+ /// Whether we are hypderdashing or not.
+ ///
+ public bool HyperDashing => hyperDashModifier != 1;
+
+ private double hyperDashModifier = 1;
+
+ ///
+ /// The direction in which hyperdash is allowed. 0 allows both directions.
+ ///
+ public double HyperDashDirection;
+
+ ///
+ /// The speed modifier resultant from hyperdash. Will trigger hyperdash when not equal to 1.
+ ///
+ public double HyperDashModifier
+ {
+ get { return hyperDashModifier; }
+ set
+ {
+ if (value == hyperDashModifier) return;
+ hyperDashModifier = value;
+
+ const float transition_length = 180;
+
+ if (HyperDashing)
+ {
+ this.FadeColour(Color4.OrangeRed, transition_length, Easing.OutQuint);
+ this.FadeTo(0.2f, transition_length, Easing.OutQuint);
+ Trail = true;
+ }
+ else
+ {
+ HyperDashDirection = 0;
+ this.FadeColour(Color4.White, transition_length, Easing.OutQuint);
+ this.FadeTo(1, transition_length, Easing.OutQuint);
+ }
+ }
+ }
+
public bool OnPressed(CatchAction action)
{
switch (action)
@@ -201,10 +298,15 @@ namespace osu.Game.Rulesets.Catch.UI
if (currentDirection == 0) return;
+ var direction = Math.Sign(currentDirection);
+
double dashModifier = Dashing ? 1 : 0.5;
- Scale = new Vector2(Math.Abs(Scale.X) * Math.Sign(currentDirection), Scale.Y);
- X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime * BASE_SPEED * dashModifier, 0, 1);
+ if (hyperDashModifier != 1 && (HyperDashDirection == 0 || direction == Math.Sign(HyperDashDirection)))
+ dashModifier = hyperDashModifier;
+
+ Scale = new Vector2(Math.Abs(Scale.X) * direction, Scale.Y);
+ X = (float)MathHelper.Clamp(X + direction * Clock.ElapsedFrameTime * BASE_SPEED * dashModifier, 0, 1);
}
private void explode()
diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj
index b7916f674e..969ee702e3 100644
--- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj
+++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj
@@ -1,6 +1,6 @@
-
+
Debug
AnyCPU
@@ -21,7 +21,6 @@
prompt
4
false
- 6
pdbonly
@@ -67,6 +66,7 @@
+
@@ -74,9 +74,6 @@
-
- osu.licenseheader
-
diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj
index 19832d733e..ec6f59b5be 100644
--- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj
+++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj
@@ -1,6 +1,6 @@
-
+
Debug
AnyCPU
@@ -21,7 +21,6 @@
prompt
4
false
- 6
pdbonly
@@ -99,9 +98,6 @@
-
- osu.licenseheader
-
diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs
index a65d28cec0..56990d1351 100644
--- a/osu.Game.Rulesets.Osu/OsuInputManager.cs
+++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System.Collections.Generic;
using System.ComponentModel;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.UI;
@@ -9,6 +10,8 @@ namespace osu.Game.Rulesets.Osu
{
public class OsuInputManager : RulesetInputManager
{
+ public IEnumerable PressedActions => KeyBindingContainer.PressedActions;
+
public OsuInputManager(RulesetInfo ruleset) : base(ruleset, 0, SimultaneousBindingMode.Unique)
{
}
diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
index bc343ece05..819b6d52a9 100644
--- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
+++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
@@ -1,6 +1,6 @@
-
+
Debug
AnyCPU
@@ -22,7 +22,6 @@
prompt
4
false
- 6
pdbonly
@@ -108,9 +107,6 @@
-
- osu.licenseheader
-
diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
index 0b4e6e43f2..72e9e6a061 100644
--- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
+++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
@@ -1,6 +1,6 @@
-
+
Debug
AnyCPU
@@ -21,7 +21,6 @@
prompt
4
false
- 6
pdbonly
@@ -96,9 +95,6 @@
-
- osu.licenseheader
-
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
new file mode 100644
index 0000000000..86413af4b6
--- /dev/null
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -0,0 +1,214 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.IO;
+using NUnit.Framework;
+using OpenTK;
+using OpenTK.Graphics;
+using osu.Game.Tests.Resources;
+using System.Linq;
+using osu.Game.Audio;
+using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Beatmaps.Formats;
+using osu.Game.Beatmaps.Timing;
+
+namespace osu.Game.Tests.Beatmaps.Formats
+{
+ [TestFixture]
+ public class LegacyBeatmapDecoderTest
+ {
+ [Test]
+ public void TestDecodeBeatmapGeneral()
+ {
+ var decoder = new LegacyBeatmapDecoder();
+ using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
+ using (var stream = new StreamReader(resStream))
+ {
+ var beatmap = decoder.DecodeBeatmap(stream);
+ var beatmapInfo = beatmap.BeatmapInfo;
+ var metadata = beatmap.Metadata;
+
+ Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", metadata.AudioFile);
+ Assert.AreEqual(0, beatmapInfo.AudioLeadIn);
+ Assert.AreEqual(164471, metadata.PreviewTime);
+ Assert.IsFalse(beatmapInfo.Countdown);
+ Assert.AreEqual(0.7f, beatmapInfo.StackLeniency);
+ Assert.IsTrue(beatmapInfo.RulesetID == 0);
+ Assert.IsFalse(beatmapInfo.LetterboxInBreaks);
+ Assert.IsFalse(beatmapInfo.SpecialStyle);
+ Assert.IsFalse(beatmapInfo.WidescreenStoryboard);
+ }
+ }
+
+ [Test]
+ public void TestDecodeBeatmapEditor()
+ {
+ var decoder = new LegacyBeatmapDecoder();
+ using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
+ using (var stream = new StreamReader(resStream))
+ {
+ var beatmapInfo = decoder.DecodeBeatmap(stream).BeatmapInfo;
+
+ int[] expectedBookmarks =
+ {
+ 11505, 22054, 32604, 43153, 53703, 64252, 74802, 85351,
+ 95901, 106450, 116999, 119637, 130186, 140735, 151285,
+ 161834, 164471, 175020, 185570, 196119, 206669, 209306
+ };
+ Assert.AreEqual(expectedBookmarks.Length, beatmapInfo.Bookmarks.Length);
+ for (int i = 0; i < expectedBookmarks.Length; i++)
+ Assert.AreEqual(expectedBookmarks[i], beatmapInfo.Bookmarks[i]);
+ Assert.AreEqual(1.8, beatmapInfo.DistanceSpacing);
+ Assert.AreEqual(4, beatmapInfo.BeatDivisor);
+ Assert.AreEqual(4, beatmapInfo.GridSize);
+ Assert.AreEqual(2, beatmapInfo.TimelineZoom);
+ }
+ }
+
+ [Test]
+ public void TestDecodeBeatmapMetadata()
+ {
+ var decoder = new LegacyBeatmapDecoder();
+ using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
+ using (var stream = new StreamReader(resStream))
+ {
+ var beatmap = decoder.DecodeBeatmap(stream);
+ var beatmapInfo = beatmap.BeatmapInfo;
+ var metadata = beatmap.Metadata;
+
+ Assert.AreEqual("Renatus", metadata.Title);
+ Assert.AreEqual("Renatus", metadata.TitleUnicode);
+ Assert.AreEqual("Soleily", metadata.Artist);
+ Assert.AreEqual("Soleily", metadata.ArtistUnicode);
+ Assert.AreEqual("Gamu", metadata.AuthorString);
+ Assert.AreEqual("Insane", beatmapInfo.Version);
+ Assert.AreEqual(string.Empty, metadata.Source);
+ Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", metadata.Tags);
+ Assert.AreEqual(557821, beatmapInfo.OnlineBeatmapID);
+ Assert.AreEqual(241526, metadata.OnlineBeatmapSetID);
+ }
+ }
+
+ [Test]
+ public void TestDecodeBeatmapDifficulty()
+ {
+ var decoder = new LegacyBeatmapDecoder();
+ using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
+ using (var stream = new StreamReader(resStream))
+ {
+ var difficulty = decoder.DecodeBeatmap(stream).BeatmapInfo.BaseDifficulty;
+
+ Assert.AreEqual(6.5f, difficulty.DrainRate);
+ Assert.AreEqual(4, difficulty.CircleSize);
+ Assert.AreEqual(8, difficulty.OverallDifficulty);
+ Assert.AreEqual(9, difficulty.ApproachRate);
+ Assert.AreEqual(1.8f, difficulty.SliderMultiplier);
+ Assert.AreEqual(2, difficulty.SliderTickRate);
+ }
+ }
+
+ [Test]
+ public void TestDecodeBeatmapEvents()
+ {
+ var decoder = new LegacyBeatmapDecoder();
+ using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
+ using (var stream = new StreamReader(resStream))
+ {
+ var beatmap = decoder.DecodeBeatmap(stream);
+ var metadata = beatmap.Metadata;
+ var breakPoint = beatmap.Breaks[0];
+
+ Assert.AreEqual("machinetop_background.jpg", metadata.BackgroundFile);
+ Assert.AreEqual(122474, breakPoint.StartTime);
+ Assert.AreEqual(140135, breakPoint.EndTime);
+ Assert.IsTrue(breakPoint.HasEffect);
+ }
+ }
+
+ [Test]
+ public void TestDecodeBeatmapTimingPoints()
+ {
+ var decoder = new LegacyBeatmapDecoder();
+ using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
+ using (var stream = new StreamReader(resStream))
+ {
+ var beatmap = decoder.DecodeBeatmap(stream);
+ var controlPoints = beatmap.ControlPointInfo;
+
+ Assert.AreEqual(4, controlPoints.TimingPoints.Count);
+ var timingPoint = controlPoints.TimingPoints[0];
+ Assert.AreEqual(956, timingPoint.Time);
+ Assert.AreEqual(329.67032967033d, timingPoint.BeatLength);
+ Assert.AreEqual(TimeSignatures.SimpleQuadruple, timingPoint.TimeSignature);
+
+ Assert.AreEqual(5, controlPoints.DifficultyPoints.Count);
+ var difficultyPoint = controlPoints.DifficultyPoints[0];
+ Assert.AreEqual(116999, difficultyPoint.Time);
+ Assert.AreEqual(0.75000000000000189d, difficultyPoint.SpeedMultiplier);
+
+ Assert.AreEqual(34, controlPoints.SoundPoints.Count);
+ var soundPoint = controlPoints.SoundPoints[0];
+ Assert.AreEqual(956, soundPoint.Time);
+ Assert.AreEqual("soft", soundPoint.SampleBank);
+ Assert.AreEqual(60, soundPoint.SampleVolume);
+
+ Assert.AreEqual(8, controlPoints.EffectPoints.Count);
+ var effectPoint = controlPoints.EffectPoints[0];
+ Assert.AreEqual(53703, effectPoint.Time);
+ Assert.IsTrue(effectPoint.KiaiMode);
+ Assert.IsFalse(effectPoint.OmitFirstBarLine);
+ }
+ }
+
+ [Test]
+ public void TestDecodeBeatmapColors()
+ {
+ var decoder = new LegacyBeatmapDecoder();
+ using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
+ using (var stream = new StreamReader(resStream))
+ {
+ var comboColors = decoder.DecodeBeatmap(stream).ComboColors;
+
+ Color4[] expectedColors =
+ {
+ new Color4(142, 199, 255, 255),
+ new Color4(255, 128, 128, 255),
+ new Color4(128, 255, 255, 255),
+ new Color4(128, 255, 128, 255),
+ new Color4(255, 187, 255, 255),
+ new Color4(255, 177, 140, 255),
+ };
+ Assert.AreEqual(expectedColors.Length, comboColors.Count);
+ for (int i = 0; i < expectedColors.Length; i++)
+ Assert.AreEqual(expectedColors[i], comboColors[i]);
+ }
+ }
+
+ [Test]
+ public void TestDecodeBeatmapHitObjects()
+ {
+ var decoder = new LegacyBeatmapDecoder();
+ using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
+ using (var stream = new StreamReader(resStream))
+ {
+ var hitObjects = decoder.DecodeBeatmap(stream).HitObjects;
+
+ var curveData = hitObjects[0] as IHasCurve;
+ var positionData = hitObjects[0] as IHasPosition;
+
+ Assert.IsNotNull(positionData);
+ Assert.IsNotNull(curveData);
+ Assert.AreEqual(new Vector2(192, 168), positionData.Position);
+ Assert.AreEqual(956, hitObjects[0].StartTime);
+ Assert.IsTrue(hitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL));
+
+ positionData = hitObjects[1] as IHasPosition;
+
+ Assert.IsNotNull(positionData);
+ Assert.AreEqual(new Vector2(304, 56), positionData.Position);
+ Assert.AreEqual(1285, hitObjects[1].StartTime);
+ Assert.IsTrue(hitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP));
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
new file mode 100644
index 0000000000..839932c640
--- /dev/null
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
@@ -0,0 +1,90 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.IO;
+using System.Linq;
+using NUnit.Framework;
+using OpenTK;
+using osu.Framework.Graphics;
+using osu.Game.Beatmaps.Formats;
+using osu.Game.Storyboards;
+using osu.Game.Tests.Resources;
+
+namespace osu.Game.Tests.Beatmaps.Formats
+{
+ [TestFixture]
+ public class LegacyStoryboardDecoderTest
+ {
+ [Test]
+ public void TestDecodeStoryboardEvents()
+ {
+ var decoder = new LegacyBeatmapDecoder();
+ using (var resStream = Resource.OpenResource("Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu"))
+ using (var stream = new StreamReader(resStream))
+ {
+ var storyboard = decoder.GetStoryboardDecoder().DecodeStoryboard(stream);
+
+ Assert.IsTrue(storyboard.HasDrawable);
+ Assert.AreEqual(4, storyboard.Layers.Count());
+
+ StoryboardLayer background = storyboard.Layers.FirstOrDefault(l => l.Depth == 3);
+ Assert.IsNotNull(background);
+ Assert.AreEqual(16, background.Elements.Count());
+ Assert.IsTrue(background.EnabledWhenFailing);
+ Assert.IsTrue(background.EnabledWhenPassing);
+ Assert.AreEqual("Background", background.Name);
+
+ StoryboardLayer fail = storyboard.Layers.FirstOrDefault(l => l.Depth == 2);
+ Assert.IsNotNull(fail);
+ Assert.AreEqual(0, fail.Elements.Count());
+ Assert.IsTrue(fail.EnabledWhenFailing);
+ Assert.IsFalse(fail.EnabledWhenPassing);
+ Assert.AreEqual("Fail", fail.Name);
+
+ StoryboardLayer pass = storyboard.Layers.FirstOrDefault(l => l.Depth == 1);
+ Assert.IsNotNull(pass);
+ Assert.AreEqual(0, pass.Elements.Count());
+ Assert.IsFalse(pass.EnabledWhenFailing);
+ Assert.IsTrue(pass.EnabledWhenPassing);
+ Assert.AreEqual("Pass", pass.Name);
+
+ StoryboardLayer foreground = storyboard.Layers.FirstOrDefault(l => l.Depth == 0);
+ Assert.IsNotNull(foreground);
+ Assert.AreEqual(151, foreground.Elements.Count());
+ Assert.IsTrue(foreground.EnabledWhenFailing);
+ Assert.IsTrue(foreground.EnabledWhenPassing);
+ Assert.AreEqual("Foreground", foreground.Name);
+
+ int spriteCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSprite));
+ int animationCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardAnimation));
+ int sampleCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSample));
+
+ Assert.AreEqual(15, spriteCount);
+ Assert.AreEqual(1, animationCount);
+ Assert.AreEqual(0, sampleCount);
+ Assert.AreEqual(background.Elements.Count(), spriteCount + animationCount + sampleCount);
+
+ var sprite = background.Elements.ElementAt(0) as StoryboardSprite;
+ Assert.NotNull(sprite);
+ Assert.IsTrue(sprite.HasCommands);
+ Assert.AreEqual(new Vector2(320, 240), sprite.InitialPosition);
+ Assert.IsTrue(sprite.IsDrawable);
+ Assert.AreEqual(Anchor.Centre, sprite.Origin);
+ Assert.AreEqual("SB/lyric/ja-21.png", sprite.Path);
+
+ var animation = background.Elements.ElementAt(12) as StoryboardAnimation;
+ Assert.NotNull(animation);
+ Assert.AreEqual(141175, animation.EndTime);
+ Assert.AreEqual(10, animation.FrameCount);
+ Assert.AreEqual(30, animation.FrameDelay);
+ Assert.IsTrue(animation.HasCommands);
+ Assert.AreEqual(new Vector2(320, 240), animation.InitialPosition);
+ Assert.IsTrue(animation.IsDrawable);
+ Assert.AreEqual(AnimationLoopType.LoopForever, animation.LoopType);
+ Assert.AreEqual(Anchor.Centre, animation.Origin);
+ Assert.AreEqual("SB/red jitter/red_0000.jpg", animation.Path);
+ Assert.AreEqual(78993, animation.StartTime);
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs
deleted file mode 100644
index 95b691e07f..0000000000
--- a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using System.IO;
-using NUnit.Framework;
-using OpenTK;
-using OpenTK.Graphics;
-using osu.Game.Beatmaps.Formats;
-using osu.Game.Tests.Resources;
-using System.Linq;
-using osu.Game.Audio;
-using osu.Game.Rulesets.Objects.Types;
-
-namespace osu.Game.Tests.Beatmaps.Formats
-{
- [TestFixture]
- public class OsuLegacyDecoderTest
- {
- [Test]
- public void TestDecodeMetadata()
- {
- var decoder = new OsuLegacyDecoder();
- using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
- {
- var beatmap = decoder.Decode(new StreamReader(stream));
- var meta = beatmap.BeatmapInfo.Metadata;
- Assert.AreEqual(241526, meta.OnlineBeatmapSetID);
- Assert.AreEqual("Soleily", meta.Artist);
- Assert.AreEqual("Soleily", meta.ArtistUnicode);
- Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile);
- Assert.AreEqual("Gamu", meta.AuthorString);
- Assert.AreEqual("machinetop_background.jpg", meta.BackgroundFile);
- Assert.AreEqual(164471, meta.PreviewTime);
- Assert.AreEqual(string.Empty, meta.Source);
- Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", meta.Tags);
- Assert.AreEqual("Renatus", meta.Title);
- Assert.AreEqual("Renatus", meta.TitleUnicode);
- }
- }
-
- [Test]
- public void TestDecodeGeneral()
- {
- var decoder = new OsuLegacyDecoder();
- using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
- {
- var beatmapInfo = decoder.Decode(new StreamReader(stream)).BeatmapInfo;
- Assert.AreEqual(0, beatmapInfo.AudioLeadIn);
- Assert.AreEqual(false, beatmapInfo.Countdown);
- Assert.AreEqual(0.7f, beatmapInfo.StackLeniency);
- Assert.AreEqual(false, beatmapInfo.SpecialStyle);
- Assert.IsTrue(beatmapInfo.RulesetID == 0);
- Assert.AreEqual(false, beatmapInfo.LetterboxInBreaks);
- Assert.AreEqual(false, beatmapInfo.WidescreenStoryboard);
- }
- }
-
- [Test]
- public void TestDecodeEditor()
- {
- var decoder = new OsuLegacyDecoder();
- using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
- {
- var beatmap = decoder.Decode(new StreamReader(stream)).BeatmapInfo;
- int[] expectedBookmarks =
- {
- 11505, 22054, 32604, 43153, 53703, 64252, 74802, 85351,
- 95901, 106450, 116999, 119637, 130186, 140735, 151285,
- 161834, 164471, 175020, 185570, 196119, 206669, 209306
- };
- Assert.AreEqual(expectedBookmarks.Length, beatmap.Bookmarks.Length);
- for (int i = 0; i < expectedBookmarks.Length; i++)
- Assert.AreEqual(expectedBookmarks[i], beatmap.Bookmarks[i]);
- Assert.AreEqual(1.8, beatmap.DistanceSpacing);
- Assert.AreEqual(4, beatmap.BeatDivisor);
- Assert.AreEqual(4, beatmap.GridSize);
- Assert.AreEqual(2, beatmap.TimelineZoom);
- }
- }
-
- [Test]
- public void TestDecodeDifficulty()
- {
- var decoder = new OsuLegacyDecoder();
- using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
- {
- var beatmap = decoder.Decode(new StreamReader(stream));
- var difficulty = beatmap.BeatmapInfo.BaseDifficulty;
- Assert.AreEqual(6.5f, difficulty.DrainRate);
- Assert.AreEqual(4, difficulty.CircleSize);
- Assert.AreEqual(8, difficulty.OverallDifficulty);
- Assert.AreEqual(9, difficulty.ApproachRate);
- Assert.AreEqual(1.8f, difficulty.SliderMultiplier);
- Assert.AreEqual(2, difficulty.SliderTickRate);
- }
- }
-
- [Test]
- public void TestDecodeColors()
- {
- var decoder = new OsuLegacyDecoder();
- using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
- {
- var beatmap = decoder.Decode(new StreamReader(stream));
- Color4[] expected =
- {
- new Color4(142, 199, 255, 255),
- new Color4(255, 128, 128, 255),
- new Color4(128, 255, 255, 255),
- new Color4(128, 255, 128, 255),
- new Color4(255, 187, 255, 255),
- new Color4(255, 177, 140, 255),
- };
- Assert.AreEqual(expected.Length, beatmap.ComboColors.Count);
- for (int i = 0; i < expected.Length; i++)
- Assert.AreEqual(expected[i], beatmap.ComboColors[i]);
- }
- }
-
- [Test]
- public void TestDecodeHitObjects()
- {
- var decoder = new OsuLegacyDecoder();
- using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
- {
- var beatmap = decoder.Decode(new StreamReader(stream));
-
- var curveData = beatmap.HitObjects[0] as IHasCurve;
- var positionData = beatmap.HitObjects[0] as IHasPosition;
-
- Assert.IsNotNull(positionData);
- Assert.IsNotNull(curveData);
- Assert.AreEqual(new Vector2(192, 168), positionData.Position);
- Assert.AreEqual(956, beatmap.HitObjects[0].StartTime);
- Assert.IsTrue(beatmap.HitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL));
-
- positionData = beatmap.HitObjects[1] as IHasPosition;
-
- Assert.IsNotNull(positionData);
- Assert.AreEqual(new Vector2(304, 56), positionData.Position);
- Assert.AreEqual(1285, beatmap.HitObjects[1].StartTime);
- Assert.IsTrue(beatmap.HitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP));
- }
- }
- }
-}
diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs
index 12bbde5b57..ffe735c89f 100644
--- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs
+++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs
@@ -50,7 +50,7 @@ namespace osu.Game.Tests.Beatmaps.IO
BeatmapMetadata meta;
using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu")))
- meta = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata;
+ meta = Decoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata;
Assert.AreEqual(241526, meta.OnlineBeatmapSetID);
Assert.AreEqual("Soleily", meta.Artist);
diff --git a/osu.Game.Tests/Resources/Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu b/osu.Game.Tests/Resources/Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu
new file mode 100644
index 0000000000..f7b33fa6a8
--- /dev/null
+++ b/osu.Game.Tests/Resources/Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu
@@ -0,0 +1,1997 @@
+osu file format v13
+
+[General]
+AudioFilename: yotsuya192.mp3
+AudioLeadIn: 0
+PreviewTime: 71220
+Countdown: 0
+SampleSet: Soft
+StackLeniency: 0.7
+Mode: 0
+LetterboxInBreaks: 0
+WidescreenStoryboard: 1
+
+[Editor]
+Bookmarks: 24456,34275,43002,53911,71366,88820,99729,117184,143366,152093,160820,169547
+DistanceSpacing: 1.4
+BeatDivisor: 4
+GridSize: 4
+TimelineZoom: 1
+
+[Metadata]
+Title:Yotsuya-san ni Yoroshiku
+TitleUnicode:四ツ谷さんによろしく
+Artist:Himeringo
+ArtistUnicode:ひめりんご
+Creator:RLC
+Version:Winber1's Extreme
+Source:
+Tags:flask nyquill winber1 skystar amamiya yuko cheesiest onosakihito utaite leave it to eight hatsune miku vocaloid
+BeatmapID:378781
+BeatmapSetID:100049
+
+[Difficulty]
+HPDrainRate:6
+CircleSize:4
+OverallDifficulty:8.5
+ApproachRate:9.5
+SliderMultiplier:1.9
+SliderTickRate:1
+
+[Events]
+//Background and Video events
+0,0,"primary.jpg",0,0
+//Break Periods
+//Storyboard Layer 0 (Background)
+Sprite,Background,Centre,"SB\lyric\ja-21.png",320,240
+ S,0,117175,,0.8
+ F,0,117175,117584,0,1
+ M,0,117175,118675,192,393,-12,393
+ F,0,117584,118402,1
+ F,0,118402,118675,1,0
+Sprite,Background,Centre,"SB\lyric\ja-22.png",320,240
+ S,0,118675,,0.8
+ F,0,118675,119084,0,1
+ M,0,118675,119629,150,393,23,393
+ F,0,119084,119357,1
+ F,0,119357,119629,1,0
+Sprite,Background,Centre,"SB\lyric\ja-22-repeat.png",320,240
+ S,0,119357,,0.8
+ M,0,119629,,196,393
+ F,0,119629,119902,0,1
+ M,0,119629,121947,196,393,-58,393
+ F,0,119902,121675,1
+ F,0,121675,121947,1,0
+Sprite,Background,Centre,"SB\lyric\ja-23.png",320,240
+ S,0,121947,,0.8
+ F,0,121947,122357,0,1
+ M,0,121947,123038,158,393,23,393
+ F,0,122357,122766,1
+ F,0,122766,123038,1,0
+Sprite,Background,Centre,"SB\lyric\ja-24.png",320,240
+ S,0,123038,,0.8
+ F,0,123038,123447,0,1
+ M,0,123038,123720,249,393,187,393
+ F,0,123447,123720,1
+Sprite,Background,Centre,"SB\lyric\en-21.png",320,240
+ S,0,117175,,0.6
+ F,0,117175,117584,0,1
+ M,0,117175,117925,641,425,551,425
+ F,0,117584,118402,1
+ M,0,117925,118675,551,425,461,425
+ F,0,118402,118675,1,0
+Sprite,Background,Centre,"SB\lyric\en-22.png",320,240
+ S,0,118675,,0.6
+ F,0,118675,119084,0,1
+ M,0,118675,119152,616,425,554,425
+ F,0,119084,119357,1
+ M,0,119152,119629,554,425,489,425
+ F,0,119357,119629,1,0
+Sprite,Background,Centre,"SB\lyric\en-22-repeat.png",320,240
+ S,0,119629,,0.6
+ F,0,119629,120038,0,1
+ M,0,119629,121947,683,425,417,425
+ F,0,120038,121675,1
+ F,0,121675,121947,1,0
+Sprite,Background,Centre,"SB\lyric\en-23.png",320,240
+ S,0,121947,,0.6
+ F,0,121947,122357,0,1
+ M,0,121947,123038,617,425,487,425
+ F,0,122357,122766,1
+ F,0,122766,123038,1,0
+Sprite,Background,Centre,"SB\lyric\en-24.png",320,240
+ S,0,123038,,0.6
+ F,0,123038,123447,0,1
+ M,0,123038,123720,515,425,446,425
+Sprite,Background,Centre,"SB\lower mask.png",320,240
+ S,0,117175,,0.625
+ R,0,117175,,0.0001
+ M,0,117175,123720,320,330
+Sprite,Background,Centre,"SB\black.jpg",320,240
+ F,0,-97,,1
+ F,0,20084,23357,1,0
+ F,0,23357,24447,0
+ F,0,24447,25470,0,1
+ F,0,25538,26629,0,1
+ F,0,27720,28811,0.5,1
+ F,0,29902,30993,0,1
+ F,0,32084,33175,0.5,1
+ F,0,34266,,0
+ F,0,70266,,1
+ S,0,70266,,1.267815
+ R,0,70266,,0.0001
+ F,0,71357,,0
+ F,0,71357,,0
+ F,0,86629,,1
+ F,0,87720,88538,1,0
+ F,0,88538,,0
+ M,0,108877,116493,314,289,312,322
+ F,0,116084,,1
+ F,0,117175,117720,0,0.5
+ F,0,117720,118266,0.5,0
+ F,0,118266,118811,0,0.5
+ F,0,118811,119357,0.5,0
+ F,0,119357,119902,0,0.5
+ F,0,119902,120447,0.5,0
+ F,0,120447,120993,0,0.5
+ F,0,120993,121538,0.5,0
+ F,0,121538,122084,0,0.5
+ F,0,122084,122629,0.5,0
+ F,0,122629,123175,0,0.5
+ F,0,123175,123686,0.5,0
+ F,0,123720,,1
+ F,0,126175,,1,0.07840011
+ F,0,141175,,1
+ F,0,143357,144447,0,1
+ F,0,145538,146629,0.5,1
+ F,0,147720,148811,0,1
+ F,0,149902,150993,0.5,1
+ F,0,150993,,1
+ F,0,152084,,0
+ F,0,171447,,1
+ F,0,171447,177720,1
+Animation,Background,Centre,"SB\red jitter\red_0000.jpg",320,240,10,30,LoopForever
+ S,0,78993,,1.001
+ R,0,78993,,0.0001
+ F,0,78993,80084,0,1
+ F,0,80084,,1
+ F,0,86629,,0
+ F,0,133538,134629,0.9180929,1
+ F,0,141175,,1
+Sprite,Background,Centre,"SB\brown.jpg",320,240
+ M,0,71357,,320,240
+ S,0,71357,,1.001
+ R,0,71357,,0.0001
+ F,0,71357,80084,1
+ F,0,80084,81175,1,0
+Sprite,Background,Centre,"SB\blue.jpg",320,240
+ S,0,126175,,1.001
+ R,0,126175,,0.0001
+ F,0,126175,134629,1
+ F,0,134629,135720,1,0
+Sprite,Background,Centre,"SB\cloud2.png",320,240
+ S,0,126175,,4.659974
+ R,0,126175,,0.0001
+ M,0,126175,135175,72,29,474,29
+ F,0,133538,135175,0.4690581,0
+//Storyboard Layer 1 (Fail)
+//Storyboard Layer 2 (Pass)
+//Storyboard Layer 3 (Foreground)
+Sprite,Foreground,Centre,"SB\lyric\ja-1.png",320,240
+ S,0,53902,,0.8
+ F,0,53902,54993,0,1
+ M,0,53902,58266,232,393,263,393
+ F,0,54993,57175,1
+ F,0,57175,58266,1,0
+Sprite,Foreground,Centre,"SB\lyric\en-1.png",320,240
+ S,0,53902,,0.6
+ F,0,53902,54993,0,1
+ M,0,53902,58266,404,425,373,425
+ F,0,54993,57175,1
+ F,0,57175,58266,1,0
+Sprite,Foreground,Centre,"SB\lyric\ja-2.png",320,240
+ S,0,58266,,0.8
+ F,0,58266,59357,0,1
+ M,0,58266,62629,232,393,263,393
+ F,0,59357,61538,1
+ F,0,61538,,1
+ F,0,61538,,1
+ F,0,61538,62357,1,0
+Sprite,Foreground,Centre,"SB\lyric\ja-3.png",320,240
+ S,0,62629,,0.8
+ F,0,62629,63720,0,1
+ M,0,62629,66447,232,393,263,393
+ F,0,63720,65357,1
+ F,0,65357,66447,1,0
+Sprite,Foreground,Centre,"SB\lyric\ja-4.png",320,240
+ S,0,66447,,0.8
+ F,0,66447,67538,0,1
+ M,0,66447,70266,232,393,263,393
+ F,0,67538,69175,1
+ F,0,69175,70266,1,0
+Sprite,Foreground,Centre,"SB\lyric\ja-5.png",320,240
+ S,0,70266,,0.5
+ M,0,70266,70402,179,335
+Sprite,Foreground,Centre,"SB\lyric\ja-5.png",320,240
+ S,0,70402,,0.8
+ M,0,70402,70538,469,297
+Sprite,Foreground,Centre,"SB\lyric\ja-5.png",320,240
+ S,0,70538,,1.1
+ M,0,70538,70675,255,158
+Sprite,Foreground,Centre,"SB\lyric\ja-5RED.png",320,240
+ S,0,70675,,1.3
+ F,0,70675,70947,1
+ M,0,70675,71220,320,240
+ F,0,70947,71357,1,0
+Sprite,Foreground,Centre,"SB\lyric\ja-6-1.png",320,240
+ S,0,71357,,0.8
+ M,0,71357,71902,320,393
+Sprite,Foreground,Centre,"SB\lyric\ja-6-2.png",320,240
+ S,0,71902,,0.8
+ M,0,71902,72447,320,393
+Sprite,Foreground,Centre,"SB\lyric\ja-6-3.png",320,240
+ S,0,72447,,0.8
+ M,0,72447,72857,320,393
+Sprite,Foreground,Centre,"SB\lyric\ja-7.png",320,240
+ S,0,72857,,0.8
+ M,0,72857,73947,320,393
+Sprite,Foreground,Centre,"SB\lyric\ja-8.png",320,240
+ S,0,73947,,0.8
+ M,0,73947,76129,320,393
+Sprite,Foreground,Centre,"SB\lyric\ja-9.png",320,240
+ S,0,76129,,0.8
+ M,0,76129,77220,320,393
+Sprite,Foreground,Centre,"SB\lyric\ja-10-2.png",320,240
+ S,0,78311,,0.8
+ M,0,78311,80084,376,393
+Sprite,Foreground,Centre,"SB\lyric\ja-10-1.png",320,240
+ S,0,77220,,0.8
+ M,0,77220,80084,264,393
+Animation,Foreground,Centre,"SB\lyric\11-1\ja-11-1_0000.png",320,240,10,30,LoopForever
+ S,0,80084,,0.8
+ M,0,80084,81584,230,326
+Animation,Foreground,Centre,"SB\lyric\11-2\ja-11-2_0000.png",320,240,10,30,LoopForever
+ S,0,80629,,1.1
+ M,0,80629,81584,325,305
+Animation,Foreground,Centre,"SB\lyric\11-3\ja-11-3_0000.png",320,240,10,30,LoopForever
+ S,0,81175,,1.3
+ M,0,81175,81584,425,284
+Animation,Foreground,Centre,"SB\lyric\12\ja-12_0000.png",320,240,10,30,LoopForever
+ S,0,81584,,1.1
+ M,0,81584,82675,306,214
+Animation,Foreground,Centre,"SB\lyric\13\ja-13_0000.png",320,240,10,30,LoopForever
+ S,0,82675,,0.8
+ M,0,82675,84857,315,294
+Animation,Foreground,Centre,"SB\lyric\14\ja-14_0000.png",320,240,10,30,LoopForever
+ S,0,84857,,1.1
+ M,0,84857,85947,320,222
+Animation,Foreground,Centre,"SB\lyric\15-1\ja-15-1_0000.png",320,240,10,30,LoopForever
+ S,0,85947,,0.8
+ M,0,85947,86629,269,393
+Sprite,Foreground,Centre,"SB\lyric\ja-15-1.png",320,240
+ S,0,86629,,0.8
+ M,0,86629,87720,269,393
+ F,0,87720,88538,1,0
+Sprite,Foreground,Centre,"SB\lyric\ja-15-2.png",320,240
+ S,0,87175,,0.8
+ M,0,87175,87720,373,393
+ F,0,87720,88538,1,0
+Sprite,Foreground,Centre,"SB\lyric\ja-16.png",320,240
+ S,0,99720,,0.8
+ F,0,99720,100811,0,1
+ M,0,99720,104084,232,393,263,393
+ F,0,100811,102993,1
+ F,0,102993,104084,1,0
+Sprite,Foreground,Centre,"SB\lyric\ja-17.png",320,240
+ S,0,104084,,0.8
+ F,0,104084,105175,0,1
+ M,0,104084,108447,232,393,263,393
+ F,0,105175,107357,1
+ F,0,107357,108175,1,0
+Sprite,Foreground,Centre,"SB\lyric\ja-18.png",320,240
+ S,0,108447,,0.8
+ F,0,108447,109538,0,1
+ M,0,108447,112266,232,393,263,393
+ F,0,109538,111175,1
+ F,0,111175,112266,1,0
+Sprite,Foreground,Centre,"SB\lyric\ja-19.png",320,240
+ S,0,112266,,0.8
+ F,0,112266,113357,0,1
+ M,0,112266,116084,232,393,263,393
+ F,0,113357,114993,1
+ F,0,114993,116084,1,0
+Sprite,Foreground,Centre,"SB\lyric\ja-20_1.png",320,240
+ S,0,116084,,0.5
+ M,0,116084,116220,478,319
+Sprite,Foreground,Centre,"SB\lyric\ja-20_1.png",320,240
+ S,0,116220,,0.8
+ M,0,116220,116357,160,257
+Sprite,Foreground,Centre,"SB\lyric\ja-20_1.png",320,240
+ S,0,116357,,1.1
+ M,0,116357,116493,393,161
+Sprite,Foreground,Centre,"SB\lyric\ja-20RED.png",320,240
+ M,0,116493,,320,240
+ S,0,116493,,1.3
+ F,0,116493,116766,1
+ F,0,116766,117175,1,0
+Sprite,Foreground,Centre,"SB\lyric\ja-24RED.png",320,240
+ M,0,123720,,187,393
+ S,0,123720,,0.8
+ F,0,123720,124811,1
+ F,0,124811,125902,1,0
+Sprite,Foreground,Centre,"SB\lyric\ja-25-1.png",320,240
+ S,0,125766,,0.8
+ M,0,125766,126175,156,312
+Sprite,Foreground,Centre,"SB\lyric\ja-25-2.png",320,240
+ S,0,126175,,0.8
+ M,0,126175,126993,156,312
+ F,0,127538,127811,1,0
+Sprite,Foreground,Centre,"SB\lyric\ja-25-3.png",320,240
+ S,0,126447,,1.1
+ R,0,126447,,-0.189046
+ M,0,126447,126993,306,288
+ F,0,127538,,1
+ F,0,127538,127811,1,0
+Sprite,Foreground,Centre,"SB\lyric\ja-25-4.png",320,240
+ M,0,126993,,388,400
+ S,0,126993,,1.3
+ R,0,126993,,0.1785437
+ F,0,127538,127811,1,0
+Sprite,Foreground,Centre,"SB\lyric\ja-26.png",320,240
+ S,0,127402,,0.8
+ F,0,127402,127538,0,1
+ M,0,127402,128084,315,201
+ R,0,127402,128084,0.1050253
+ F,0,127538,128084,1
+ M,0,128084,128493,315,201,315,236
+ F,0,128084,128493,1,0
+ R,0,128084,128493,0.1050253,0.3360815
+Sprite,Foreground,Centre,"SB\lyric\ja-27.png",320,240
+ S,0,128493,,0.9
+ F,0,128493,128629,0,1
+ M,0,128493,130266,191,426
+ R,0,128493,130266,0.06301453
+ F,0,128629,130266,1
+ M,0,130266,130675,191,426,191,458
+ F,0,130266,130675,1,0
+ R,0,130266,130675,0.06301453,0.2835687
+Sprite,Foreground,Centre,"SB\lyric\ja-28.png",320,240
+ S,0,130675,,0.8
+ R,0,130675,,-0.1155283
+ F,0,130675,130811,0,1
+ M,0,130675,131357,320,240
+ F,0,130811,131357,1
+ F,0,131357,131766,1,0
+Sprite,Foreground,Centre,"SB\lyric\ja-29.png",320,240
+ S,0,131766,,0.8
+ F,0,131766,131902,0,1
+ M,0,131766,133538,230,154
+ R,0,131766,133538,0.1260309
+ F,0,131902,133538,1
+ M,0,133538,134629,230,154,230,206
+ F,0,133538,134629,1,0
+ R,0,133538,134629,0.1260309,0.2835693
+Animation,Foreground,Centre,"SB\lyric\12\ja-12_0000.png",320,240,10,30,LoopForever
+ S,0,136129,,1.1
+ M,0,136129,137220,306,214
+Animation,Foreground,Centre,"SB\lyric\13\ja-13_0000.png",320,240,10,30,LoopForever
+ S,0,137220,,0.8
+ M,0,137220,139402,315,294
+Animation,Foreground,Centre,"SB\lyric\14\ja-14_0000.png",320,240,10,30,LoopForever
+ S,0,139402,,1.1
+ M,0,139402,140493,320,222
+Animation,Foreground,Centre,"SB\lyric\30-1\ja-30-1_0000.png",320,240,10,30,LoopForever
+ S,0,134629,,0.8
+ M,0,134629,136129,153,292
+Animation,Foreground,Centre,"SB\lyric\30-2\ja-30-2_0000.png",320,240,10,30,LoopForever
+ S,0,135175,,1.1
+ M,0,135175,136129,333,216
+Animation,Foreground,Centre,"SB\lyric\30-3\ja-30-3_0000.png",320,240,10,30,LoopForever
+ S,0,135720,,1.3
+ M,0,135720,136129,485,133
+Animation,Foreground,Centre,"SB\lyric\34-1\ja-34-1_0000.png",320,240,10,30,LoopForever
+ S,0,140493,,0.8
+ M,0,140493,141175,269,393
+Sprite,Foreground,Centre,"SB\lyric\ja-34-1.png",320,240
+ S,0,141175,,0.8
+ M,0,141175,143357,269,393
+ F,0,142266,143357,1,0
+Sprite,Foreground,Centre,"SB\lyric\ja-34-2.png",320,240
+ S,0,141720,,0.8
+ M,0,141720,143357,391,393
+ F,0,142266,143357,1,0
+Sprite,Foreground,Centre,"SB\lyric\en-2.png",320,240
+ S,0,58266,,0.6
+ F,0,58266,59357,0,1
+ M,0,58266,62629,404,425,373,425
+ F,0,59357,61538,1
+ F,0,61538,62357,1,0
+Sprite,Foreground,Centre,"SB\lyric\en-3.png",320,240
+ S,0,62629,,0.6
+ F,0,62629,63720,0,1
+ M,0,62629,66447,404,425,373,425
+ F,0,63720,65357,1
+ F,0,65357,66447,1,0
+Sprite,Foreground,Centre,"SB\lyric\en-4.png",320,240
+ S,0,66447,,0.6
+ F,0,66447,67538,0,1
+ M,0,66447,70266,404,425,373,425
+ F,0,67538,69175,1
+ F,0,69175,70266,1,0
+Sprite,Foreground,Centre,"SB\lyric\en-5.png",320,240
+ F,0,70266,,1
+ S,0,70266,,0.4
+ M,0,70266,70402,436,192
+Sprite,Foreground,Centre,"SB\lyric\en-5.png",320,240
+ S,0,70402,,0.6
+ M,0,70402,70538,181,97
+Sprite,Foreground,Centre,"SB\lyric\en-5.png",320,240
+ S,0,70538,,0.8
+ M,0,70538,70675,391,392
+Sprite,Foreground,Centre,"SB\lyric\en-5RED.png",320,240
+ M,0,70675,,320,294
+ F,0,70675,70947,1
+ F,0,70947,71357,1,0
+Sprite,Foreground,Centre,"SB\lyric\en-6-1.png",320,240
+ S,0,71357,,0.7885935
+ M,0,71357,71902,320,425
+Sprite,Foreground,Centre,"SB\lyric\en-6-2.png",320,240
+ S,0,71902,,0.7357419
+ M,0,71902,72447,320,425
+Sprite,Foreground,Centre,"SB\lyric\en-6-3.png",320,240
+ S,0,72447,,0.8084131
+ M,0,72447,72857,320,425
+Sprite,Foreground,Centre,"SB\lyric\en-7.png",320,240
+ S,0,72857,,0.8018063
+ M,0,72857,73947,320,425
+Sprite,Foreground,Centre,"SB\lyric\en-8.png",320,240
+ S,0,73947,,0.6961035
+ M,0,73947,76129,320,425
+Sprite,Foreground,Centre,"SB\lyric\en-9.png",320,240
+ S,0,76129,,0.762168
+ M,0,76129,77220,320,425
+Sprite,Foreground,Centre,"SB\lyric\en-10-1.png",320,240
+ S,0,77220,,0.6564645
+ M,0,77220,80084,262,425
+Sprite,Foreground,Centre,"SB\lyric\en-10-2.png",320,240
+ S,0,78311,,0.5837936
+ M,0,78311,80084,411,425
+Sprite,Foreground,Centre,"SB\lyric\en-16.png",320,240
+ S,0,99720,,0.6
+ F,0,99720,100811,0,1
+ M,0,99720,104084,404,425,373,425
+ F,0,100811,102993,1
+ F,0,102993,104084,1,0
+Sprite,Foreground,Centre,"SB\lyric\en-17-1.png",320,240
+ S,0,104084,,0.6
+ F,0,104084,105175,0,1
+ M,0,104084,108447,404,425,373,425
+ F,0,105175,107357,1
+ F,0,107357,108175,1,0
+Sprite,Foreground,Centre,"SB\lyric\en-17-2.png",320,240
+ S,0,104084,,0.6
+ F,0,104084,105175,0,1
+ M,0,104084,108175,232,457,263,457
+ F,0,105175,107357,1
+ F,0,107357,108175,1,0
+Sprite,Foreground,Centre,"SB\lyric\en-18.png",320,240
+ S,0,108447,,0.6
+ F,0,108447,109538,0,1
+ M,0,108447,112266,404,425,373,425
+ F,0,109538,111175,1
+ F,0,111175,112266,1,0
+Sprite,Foreground,Centre,"SB\lyric\en-19.png",320,240
+ S,0,112266,,0.6
+ F,0,112266,113357,0,1
+ M,0,112266,116084,404,425,373,425
+ F,0,113357,114993,1
+ F,0,114993,116084,1,0
+Sprite,Foreground,Centre,"SB\lyric\en-20-1.png",320,240
+ S,0,116084,,0.4
+ M,0,116084,116220,205,167
+Sprite,Foreground,Centre,"SB\lyric\en-20-1.png",320,240
+ S,0,116220,,0.6
+ M,0,116220,116357,389,252
+Sprite,Foreground,Centre,"SB\lyric\en-20-1.png",320,240
+ S,0,116357,,0.8
+ M,0,116357,116493,220,395
+Sprite,Foreground,Centre,"SB\lyric\en-20RED.png",320,240
+ M,0,116493,,320,293
+ F,0,116493,116766,1
+ F,0,116766,117175,1,0
+Sprite,Foreground,Centre,"SB\lyric\en-24RED.png",320,240
+ M,0,123720,,446,425
+ S,0,123720,,0.6
+ F,0,123720,124811,1
+ F,0,124811,125902,1,0
+Sprite,Foreground,Centre,"SB\lyric\en-25-1.png",320,240
+ S,0,125766,,0.6
+ M,0,125766,126175,408,240
+Sprite,Foreground,Centre,"SB\lyric\en-25-2.png",320,240
+ S,0,126175,,0.6
+ M,0,126175,126993,408,240
+ F,0,127538,127811,1,0
+Sprite,Foreground,Centre,"SB\lyric\en-25-3.png",320,240
+ M,0,126447,,525,277
+ S,0,126447,,0.8
+ R,0,126447,,0.189046
+ F,0,127538,127811,1,0
+Sprite,Foreground,Centre,"SB\lyric\en-25-4.png",320,240
+ M,0,126993,,219,400
+ S,0,126993,,1
+ R,0,126993,,-0.1260309
+ F,0,126993,127402,1,0
+Sprite,Foreground,Centre,"SB\lyric\en-26.png",320,240
+ S,0,127402,,0.7
+ F,0,127402,127538,0,1
+ M,0,127402,128084,416,325
+ R,0,127402,128084,-0.1680408
+ F,0,127538,128084,1
+ M,0,128084,128493,416,325,416,391
+ F,0,128084,128493,1,0
+ R,0,128084,128493,-0.1680408,-0.3990967
+Sprite,Foreground,Centre,"SB\lyric\en-27.png",320,240
+ S,0,128493,,0.6
+ F,0,128493,128629,0,1
+ M,0,128493,130266,443,226
+ R,0,128493,130266,-0.08402039
+ F,0,128629,130266,1
+ M,0,130266,130675,443,226,443,252
+ F,0,130266,130675,1,0
+ R,0,130266,130675,-0.08402039,-0.2310565
+Sprite,Foreground,Centre,"SB\lyric\en-28.png",320,240
+ S,0,130675,,0.7
+ R,0,130675,,0.273067
+ F,0,130675,130811,0,1
+ M,0,130675,131357,140,303
+ F,0,130811,131357,1
+ F,0,131357,131766,1,0
+Sprite,Foreground,Centre,"SB\lyric\en-29.png",320,240
+ S,0,131766,,0.7
+ F,0,131766,131902,0,1
+ M,0,131766,133538,438,257
+ R,0,131766,133538,-0.1890472
+ F,0,131902,133538,1
+ M,0,133538,134629,438,257,438,314
+ F,0,133538,134629,1,0
+ R,0,133538,134629,-0.1890472,-0.4306067
+Animation,Foreground,Centre,"SB\lyric\11-1\en-11-1_0000.png",320,240,10,30,LoopForever
+ S,0,80357,,0.6
+ M,0,80357,81584,336,384
+Animation,Foreground,Centre,"SB\lyric\11-2\en-11-2_0000.png",320,240,10,30,LoopForever
+ S,0,80902,,0.8
+ M,0,80902,81993,142,245
+Animation,Foreground,Centre,"SB\lyric\11-3\en-11-3_0000.png",320,240,10,30,LoopForever
+ S,0,81447,,1
+ M,0,81447,81993,413,164
+Animation,Foreground,Centre,"SB\lyric\12\en-12_0000.png",320,240,10,30,LoopForever
+ S,0,81993,,0.8
+ M,0,81993,83357,255,360
+Animation,Foreground,Centre,"SB\lyric\13\en-13_0000.png",320,240,10,30,LoopForever
+ S,0,83357,,0.6
+ M,0,83357,85538,222,140
+Animation,Foreground,Centre,"SB\lyric\14\en-14_0000.png",320,240,10,30,LoopForever
+ S,0,85538,,1
+ M,0,85538,86357,437,300
+Animation,Foreground,Centre,"SB\lyric\15-1\en-15-1_0000.png",320,240,10,30,LoopForever
+ F,0,86357,,1
+ S,0,86357,,0.6564642
+ M,0,86357,86629,237,430
+Sprite,Foreground,Centre,"SB\lyric\en-15-1.png",320,240
+ S,0,86629,,0.6630707
+ M,0,86629,87720,234,430
+ F,0,86629,87720,1
+ F,0,87720,88538,1,0
+Sprite,Foreground,Centre,"SB\lyric\en-15-2.png",320,240
+ S,0,87175,,0.7423482
+ M,0,87175,87720,417,430
+ F,0,87175,87720,1
+ F,0,87720,88538,1,0
+Animation,Foreground,Centre,"SB\lyric\30-1\en-30-1_0000.png",320,240,10,30,LoopForever
+ F,0,134902,,1
+ S,0,134902,,0.6
+ M,0,134902,136129,419,374
+Animation,Foreground,Centre,"SB\lyric\30-2\en-30-2_0000.png",320,240,10,30,LoopForever
+ S,0,135447,,0.6
+ M,0,135447,136811,156,164
+Animation,Foreground,Centre,"SB\lyric\30-3\en-30-3_0000.png",320,240,10,30,LoopForever
+ S,0,135993,,0.6
+ M,0,135993,136811,128,404
+Animation,Foreground,Centre,"SB\lyric\12\en-12_0000.png",320,240,10,30,LoopForever
+ S,0,136811,,0.8
+ M,0,136811,137902,439,391
+Animation,Foreground,Centre,"SB\lyric\13\en-13_0000.png",320,240,10,30,LoopForever
+ S,0,137902,,0.6
+ M,0,137902,140084,450,142
+Animation,Foreground,Centre,"SB\lyric\14\en-14_0000.png",320,240,10,30,LoopForever
+ S,0,140084,,1
+ M,0,140084,140902,191,294
+Animation,Foreground,Centre,"SB\lyric\34-1\en-34-1_0000.png",320,240,10,30,LoopForever
+ S,0,140902,,0.6
+ M,0,140902,141175,286,425
+Sprite,Foreground,Centre,"SB\lyric\en-34-1-1.png",320,240
+ S,0,141175,,0.6
+ M,0,141175,142266,286,425
+ F,0,142266,143357,1,0
+Sprite,Foreground,Centre,"SB\lyric\en-34-1-2.png",320,240
+ S,0,141175,,0.6
+ M,0,141175,142266,286,425
+ F,0,142266,143357,1,0
+Sprite,Foreground,Centre,"SB\lyric\en-34-2.png",320,240
+ S,0,141720,,0.6
+ M,0,141720,142266,451,425
+ F,0,142266,143357,1,0
+Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-1.png",320,240
+ S,0,25538,,0.6
+ M,0,25538,27720,50,240
+ M,0,27720,27857,50,240,50,247
+ F,0,27720,28538,1,0
+ R,0,27720,28538,0,0.3567487
+ M,0,27857,27993,50,247,50,263
+ M,0,27993,28129,50,263,50,290
+ M,0,28129,28266,50,290,50,321
+ M,0,28266,28402,50,321,50,360
+ M,0,28402,28538,50,360,50,408
+Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-2.png",320,240
+ S,0,25538,,0.6
+ M,0,25538,28947,104,240
+ M,0,28947,29084,104,240,104,248
+ F,0,28947,29766,1,0
+ R,0,28947,29766,0,-0.4095996
+ M,0,29084,29220,104,248,104,263
+ M,0,29220,29357,104,263,104,287
+ M,0,29357,29493,104,287,104,320
+ M,0,29493,29629,104,320,104,360
+ M,0,29629,29766,104,360,104,409
+Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-3.png",320,240
+ S,0,25538,,0.6
+ M,0,25538,28129,158,240
+ M,0,28129,28266,158,240,158,249
+ F,0,28129,28947,1,0
+ R,0,28129,28947,0,-0.2114062
+ M,0,28266,28402,158,249,158,263
+ M,0,28402,28538,158,263,158,288
+ M,0,28538,28675,158,288,158,321
+ M,0,28675,28811,158,321,158,360
+ M,0,28811,28947,158,360,158,409
+Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-4.png",320,240
+ S,0,25538,,0.6
+ M,0,25538,28811,212,240
+ M,0,28811,28947,212,240,212,248
+ F,0,28811,29629,1,0
+ R,0,28811,29629,0,0.4624512
+ M,0,28947,29084,212,248,212,263
+ M,0,29084,29220,212,263,212,288
+ M,0,29220,29357,212,288,212,320
+ M,0,29357,29493,212,320,212,360
+ M,0,29493,29629,212,360,212,407
+Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-5.png",320,240
+ S,0,25538,,0.6
+ M,0,25538,28538,266,240
+ M,0,28538,28675,266,240,266,248
+ F,0,28538,29357,1,0
+ R,0,28538,29357,0,0.2906836
+ M,0,28675,28811,266,248,266,264
+ M,0,28811,28947,266,264,266,288
+ M,0,28947,29084,266,288,266,321
+ M,0,29084,29220,266,321,266,358
+ M,0,29220,29357,266,358,266,407
+Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-6.png",320,240
+ S,0,25538,,0.6
+ M,0,25538,27993,320,240
+ M,0,27993,28129,320,240,320,247
+ F,0,27993,28811,1,0
+ R,0,27993,28811,0,0.2906836
+ M,0,28129,28266,320,247,320,263
+ M,0,28266,28402,320,263,320,288
+ M,0,28402,28538,320,288,320,321
+ M,0,28538,28675,320,321,320,360
+ M,0,28675,28811,320,360,320,408
+Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-7.png",320,240
+ S,0,25538,,0.6
+ M,0,25538,28266,374,240
+ M,0,28266,28402,374,240,374,248
+ F,0,28266,29084,1,0
+ R,0,28266,29084,0,0.1717682
+ M,0,28402,28538,374,248,374,264
+ M,0,28538,28675,374,264,374,288
+ M,0,28675,28811,374,288,374,320
+ M,0,28811,28947,374,320,374,359
+ M,0,28947,29084,374,359,374,409
+Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-8.png",320,240
+ S,0,25538,,0.6
+ M,0,25538,27857,428,240
+ M,0,27857,27993,428,240,428,249
+ F,0,27857,28675,1,0
+ R,0,27857,28675,0,-0.237832
+ M,0,27993,28129,428,249,428,263
+ M,0,28129,28266,428,263,428,288
+ M,0,28266,28402,428,288,428,320
+ M,0,28402,28538,428,320,428,358
+ M,0,28538,28675,428,358,428,408
+Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-9.png",320,240
+ S,0,25538,,0.6
+ M,0,25538,28675,482,240
+ M,0,28675,28811,482,240,482,247
+ F,0,28675,29493,1,0
+ R,0,28675,29493,0,-0.5020905
+ M,0,28811,28947,482,247,482,263
+ M,0,28947,29084,482,263,482,287
+ M,0,29084,29220,482,287,482,320
+ M,0,29220,29357,482,320,482,360
+ M,0,29357,29493,482,360,482,407
+Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-10-1.png",320,240
+ S,0,25538,,0.6
+ M,0,25538,28402,536,240
+ M,0,28402,28538,536,240,536,247
+ F,0,28402,29220,1,0
+ R,0,28402,29220,0,-0.3038965
+ M,0,28538,28675,536,247,536,263
+ M,0,28675,28811,536,263,536,289
+ M,0,28811,28947,536,289,536,321
+ M,0,28947,29084,536,321,536,360
+ M,0,29084,29220,536,360,536,407
+Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-11.png",320,240
+ S,0,25538,,0.6
+ M,0,25538,29084,590,240
+ F,0,28947,29902,1,0
+ R,0,28947,29902,0,0.6474323
+ M,0,29084,29220,590,240,590,247
+ M,0,29220,29357,590,247,590,263
+ M,0,29357,29493,590,263,590,288
+ M,0,29493,29629,590,288,590,320
+ M,0,29629,29766,590,320,590,361
+ M,0,29766,29902,590,361,590,408
+Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-1.png",320,240
+ S,0,29902,,0.6
+ M,0,29902,32084,50,240
+ M,0,32084,32221,50,240,50,247
+ F,0,32084,32902,1,0
+ R,0,32084,32902,0,0.4
+ M,0,32221,32357,50,247,50,263
+ M,0,32357,32493,50,263,50,290
+ M,0,32493,32630,50,290,50,321
+ M,0,32630,32766,50,321,50,360
+ M,0,32766,32902,50,360,50,408
+Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-2.png",320,240
+ S,0,29902,,0.6
+ M,0,29902,33311,104,240
+ M,0,33311,33448,104,240,104,248
+ F,0,33311,34130,1,0
+ R,0,33311,34130,0,-0.4
+ M,0,33448,33584,104,248,104,263
+ M,0,33584,33721,104,263,104,287
+ M,0,33721,33857,104,287,104,320
+ M,0,33857,33993,104,320,104,360
+ M,0,33993,34130,104,360,104,409
+Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-3.png",320,240
+ S,0,29902,,0.6
+ M,0,29902,32493,158,240
+ M,0,32493,32630,158,240,158,249
+ F,0,32493,33311,1,0
+ R,0,32493,33311,0,-0.4
+ M,0,32630,32766,158,249,158,263
+ M,0,32766,32902,158,263,158,288
+ M,0,32902,33039,158,288,158,321
+ M,0,33039,33175,158,321,158,360
+ M,0,33175,33311,158,360,158,409
+Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-4.png",320,240
+ S,0,29902,,0.6
+ M,0,29902,33175,212,240
+ M,0,33175,33311,212,240,212,248
+ F,0,33175,33993,1,0
+ R,0,33175,33993,0,0.4
+ M,0,33311,33448,212,248,212,263
+ M,0,33448,33584,212,263,212,288
+ M,0,33584,33721,212,288,212,320
+ M,0,33721,33857,212,320,212,360
+ M,0,33857,33993,212,360,212,407
+Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-5.png",320,240
+ S,0,29902,,0.6
+ M,0,29902,32902,266,240
+ M,0,32902,33039,266,240,266,248
+ F,0,32902,33721,1,0
+ R,0,32902,33721,0,0.4
+ M,0,33039,33175,266,248,266,264
+ M,0,33175,33311,266,264,266,288
+ M,0,33311,33448,266,288,266,321
+ M,0,33448,33584,266,321,266,358
+ M,0,33584,33721,266,358,266,407
+Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-6.png",320,240
+ S,0,29902,,0.6
+ M,0,29902,32357,320,240
+ M,0,32357,32493,320,240,320,247
+ F,0,32357,33175,1,0
+ R,0,32357,33175,0,0.4
+ M,0,32493,32630,320,247,320,263
+ M,0,32630,32766,320,263,320,288
+ M,0,32766,32902,320,288,320,321
+ M,0,32902,33039,320,321,320,360
+ M,0,33039,33175,320,360,320,408
+Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-7.png",320,240
+ S,0,29902,,0.6
+ M,0,29902,32630,374,240
+ M,0,32630,32766,374,240,374,248
+ F,0,32630,33448,1,0
+ R,0,32630,33448,0,0.4
+ M,0,32766,32902,374,248,374,264
+ M,0,32902,33039,374,264,374,288
+ M,0,33039,33175,374,288,374,320
+ M,0,33175,33311,374,320,374,359
+ M,0,33311,33448,374,359,374,409
+Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-8.png",320,240
+ S,0,29902,,0.6
+ M,0,29902,32221,428,240
+ M,0,32221,32357,428,240,428,249
+ F,0,32221,33039,1,0
+ R,0,32221,33039,0,-0.4
+ M,0,32357,32493,428,249,428,263
+ M,0,32493,32630,428,263,428,288
+ M,0,32630,32766,428,288,428,320
+ M,0,32766,32902,428,320,428,358
+ M,0,32902,33039,428,358,428,408
+Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-9.png",320,240
+ S,0,29902,,0.6
+ M,0,29902,33039,482,240
+ M,0,33039,33175,482,240,482,247
+ F,0,33039,33857,1,0
+ R,0,33039,33857,0,-0.4
+ M,0,33175,33311,482,247,482,263
+ M,0,33311,33448,482,263,482,287
+ M,0,33448,33584,482,287,482,320
+ M,0,33584,33721,482,320,482,360
+ M,0,33721,33857,482,360,482,407
+Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-10-1.png",320,240
+ S,0,29902,,0.6
+ M,0,29902,32766,536,240
+ M,0,32766,32902,536,240,536,247
+ F,0,32766,33584,1,0
+ R,0,32766,33584,0,-0.4
+ M,0,32902,33039,536,247,536,263
+ M,0,33039,33175,536,263,536,289
+ M,0,33175,33311,536,289,536,321
+ M,0,33311,33448,536,321,536,360
+ M,0,33448,33584,536,360,536,407
+Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-11.png",320,240
+ S,0,29902,,0.6
+ M,0,29902,33448,590,240
+ F,0,33311,34266,1,0
+ R,0,33311,34266,0,0.4
+ M,0,33448,33584,590,240,590,247
+ M,0,33584,33721,590,247,590,263
+ M,0,33721,33857,590,263,590,288
+ M,0,33857,33993,590,288,590,320
+ M,0,33993,34130,590,320,590,361
+ M,0,34130,34266,590,361,590,408
+Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-1.png",320,240
+ S,0,143357,,0.6
+ M,0,143357,145539,50,240
+ M,0,145539,145676,50,240,50,247
+ F,0,145539,146357,1,0
+ R,0,145539,146357,0,0.4
+ M,0,145676,145812,50,247,50,263
+ M,0,145812,145948,50,263,50,290
+ M,0,145948,146085,50,290,50,321
+ M,0,146085,146221,50,321,50,360
+ M,0,146221,146357,50,360,50,408
+Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-2.png",320,240
+ S,0,143357,,0.6
+ M,0,143357,146766,104,240
+ M,0,146766,146903,104,240,104,248
+ F,0,146766,147585,1,0
+ R,0,146766,147585,0,-0.4
+ M,0,146903,147039,104,248,104,263
+ M,0,147039,147176,104,263,104,287
+ M,0,147176,147312,104,287,104,320
+ M,0,147312,147448,104,320,104,360
+ M,0,147448,147585,104,360,104,409
+Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-3.png",320,240
+ S,0,143357,,0.6
+ M,0,143357,145948,158,240
+ M,0,145948,146085,158,240,158,249
+ F,0,145948,146766,1,0
+ R,0,145948,146766,0,-0.4
+ M,0,146085,146221,158,249,158,263
+ M,0,146221,146357,158,263,158,288
+ M,0,146357,146494,158,288,158,321
+ M,0,146494,146630,158,321,158,360
+ M,0,146630,146766,158,360,158,409
+Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-4.png",320,240
+ S,0,143357,,0.6
+ M,0,143357,146630,212,240
+ M,0,146630,146766,212,240,212,248
+ F,0,146630,147448,1,0
+ R,0,146630,147448,0,0.4
+ M,0,146766,146903,212,248,212,263
+ M,0,146903,147039,212,263,212,288
+ M,0,147039,147176,212,288,212,320
+ M,0,147176,147312,212,320,212,360
+ M,0,147312,147448,212,360,212,407
+Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-5.png",320,240
+ S,0,143357,,0.6
+ M,0,143357,146357,266,240
+ M,0,146357,146494,266,240,266,248
+ F,0,146357,147176,1,0
+ R,0,146357,147176,0,0.4
+ M,0,146494,146630,266,248,266,264
+ M,0,146630,146766,266,264,266,288
+ M,0,146766,146903,266,288,266,321
+ M,0,146903,147039,266,321,266,358
+ M,0,147039,147176,266,358,266,407
+Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-6.png",320,240
+ S,0,143357,,0.6
+ M,0,143357,145812,320,240
+ M,0,145812,145948,320,240,320,247
+ F,0,145812,146630,1,0
+ R,0,145812,146630,0,0.4
+ M,0,145948,146085,320,247,320,263
+ M,0,146085,146221,320,263,320,288
+ M,0,146221,146357,320,288,320,321
+ M,0,146357,146494,320,321,320,360
+ M,0,146494,146630,320,360,320,408
+Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-7.png",320,240
+ S,0,143357,,0.6
+ M,0,143357,146085,374,240
+ M,0,146085,146221,374,240,374,248
+ F,0,146085,146903,1,0
+ R,0,146085,146903,0,0.4
+ M,0,146221,146357,374,248,374,264
+ M,0,146357,146494,374,264,374,288
+ M,0,146494,146630,374,288,374,320
+ M,0,146630,146766,374,320,374,359
+ M,0,146766,146903,374,359,374,409
+Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-8.png",320,240
+ S,0,143357,,0.6
+ M,0,143357,145676,428,240
+ M,0,145676,145812,428,240,428,249
+ F,0,145676,146494,1,0
+ R,0,145676,146494,0,-0.4
+ M,0,145812,145948,428,249,428,263
+ M,0,145948,146085,428,263,428,288
+ M,0,146085,146221,428,288,428,320
+ M,0,146221,146357,428,320,428,358
+ M,0,146357,146494,428,358,428,408
+Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-9.png",320,240
+ S,0,143357,,0.6
+ M,0,143357,146494,482,240
+ M,0,146494,146630,482,240,482,247
+ F,0,146494,147312,1,0
+ R,0,146494,147312,0,-0.4
+ M,0,146630,146766,482,247,482,263
+ M,0,146766,146903,482,263,482,287
+ M,0,146903,147039,482,287,482,320
+ M,0,147039,147176,482,320,482,360
+ M,0,147176,147312,482,360,482,407
+Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-10-1.png",320,240
+ S,0,143357,,0.6
+ M,0,143357,146221,536,240
+ M,0,146221,146357,536,240,536,247
+ F,0,146221,147039,1,0
+ R,0,146221,147039,0,-0.4
+ M,0,146357,146494,536,247,536,263
+ M,0,146494,146630,536,263,536,289
+ M,0,146630,146766,536,289,536,321
+ M,0,146766,146903,536,321,536,360
+ M,0,146903,147039,536,360,536,407
+Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-11.png",320,240
+ S,0,143357,,0.6
+ M,0,143357,146903,590,240
+ F,0,146766,147721,1,0
+ R,0,146766,147721,0,0.4
+ M,0,146903,147039,590,240,590,247
+ M,0,147039,147176,590,247,590,263
+ M,0,147176,147312,590,263,590,288
+ M,0,147312,147448,590,288,590,320
+ M,0,147448,147585,590,320,590,361
+ M,0,147585,147721,590,361,590,408
+Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-1.png",320,240
+ S,0,147720,,0.6
+ M,0,147720,150993,104,240
+ M,0,150993,151130,104,240,104,248
+ F,0,150993,151812,1,0
+ R,0,150993,151812,0,-0.4
+ M,0,151130,151266,104,248,104,263
+ M,0,151266,151403,104,263,104,287
+ M,0,151403,151539,104,287,104,320
+ M,0,151539,151675,104,320,104,360
+ M,0,151675,151812,104,360,104,409
+Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-2.png",320,240
+ S,0,147720,,0.6
+ M,0,147720,150175,158,240
+ M,0,150175,150312,158,240,158,249
+ F,0,150175,150993,1,0
+ R,0,150175,150993,0,-0.4
+ M,0,150312,150448,158,249,158,263
+ M,0,150448,150584,158,263,158,288
+ M,0,150584,150721,158,288,158,321
+ M,0,150721,150857,158,321,158,360
+ M,0,150857,150993,158,360,158,409
+Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-3.png",320,240
+ S,0,147720,,0.6
+ M,0,147720,150857,212,240
+ M,0,150857,150993,212,240,212,248
+ F,0,150857,151675,1,0
+ R,0,150857,151675,0,0.4
+ M,0,150993,151130,212,248,212,263
+ M,0,151130,151266,212,263,212,288
+ M,0,151266,151403,212,288,212,320
+ M,0,151403,151539,212,320,212,360
+ M,0,151539,151675,212,360,212,407
+Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-4.png",320,240
+ S,0,147720,,0.6
+ M,0,147720,150584,266,240
+ M,0,150584,150721,266,240,266,248
+ F,0,150584,151403,1,0
+ R,0,150584,151403,0,0.4
+ M,0,150721,150857,266,248,266,264
+ M,0,150857,150993,266,264,266,288
+ M,0,150993,151130,266,288,266,321
+ M,0,151130,151266,266,321,266,358
+ M,0,151266,151403,266,358,266,407
+Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-5.png",320,240
+ S,0,147720,,0.6
+ M,0,147720,150039,320,240
+ M,0,150039,150175,320,240,320,247
+ F,0,150039,150857,1,0
+ R,0,150039,150857,0,0.4
+ M,0,150175,150312,320,247,320,263
+ M,0,150312,150448,320,263,320,288
+ M,0,150448,150584,320,288,320,321
+ M,0,150584,150721,320,321,320,360
+ M,0,150721,150857,320,360,320,408
+Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-6.png",320,240
+ S,0,147720,,0.6
+ M,0,147720,150312,374,240
+ M,0,150312,150448,374,240,374,248
+ F,0,150312,151130,1,0
+ R,0,150312,151130,0,0.4
+ M,0,150448,150584,374,248,374,264
+ M,0,150584,150721,374,264,374,288
+ M,0,150721,150857,374,288,374,320
+ M,0,150857,150993,374,320,374,359
+ M,0,150993,151130,374,359,374,409
+Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-7.png",320,240
+ S,0,147720,,0.6
+ M,0,147720,149903,428,240
+ M,0,149903,150039,428,240,428,249
+ F,0,149903,150721,1,0
+ R,0,149903,150721,0,-0.4
+ M,0,150039,150175,428,249,428,263
+ M,0,150175,150312,428,263,428,288
+ M,0,150312,150448,428,288,428,320
+ M,0,150448,150584,428,320,428,358
+ M,0,150584,150721,428,358,428,408
+Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-8.png",320,240
+ S,0,147720,,0.6
+ M,0,147720,150721,482,240
+ M,0,150721,150857,482,240,482,247
+ F,0,150721,151539,1,0
+ R,0,150721,151539,0,-0.4
+ M,0,150857,150993,482,247,482,263
+ M,0,150993,151130,482,263,482,287
+ M,0,151130,151266,482,287,482,320
+ M,0,151266,151403,482,320,482,360
+ M,0,151403,151539,482,360,482,407
+Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-9.png",320,240
+ S,0,147720,,0.6
+ M,0,147720,150448,536,240
+ M,0,150448,150584,536,240,536,247
+ F,0,150448,151266,1,0
+ R,0,150448,151266,0,-0.4
+ M,0,150584,150721,536,247,536,263
+ M,0,150721,150857,536,263,536,289
+ M,0,150857,150993,536,289,536,321
+ M,0,150993,151130,536,321,536,360
+ M,0,151130,151266,536,360,536,407
+Sprite,Foreground,Centre,"SB\lyric\y1\intro-en1.png",320,240
+ S,0,25538,,0.8
+ M,0,25538,27720,320,280
+ F,0,27720,27993,1,0
+Sprite,Foreground,Centre,"SB\lyric\y2\intro-en2.png",320,240
+ M,0,29902,,320,280
+ S,0,29902,,0.8
+ F,0,32084,32357,1,0
+Sprite,Foreground,Centre,"SB\lyric\z\outro-en1.png",320,240
+ M,0,143357,,320,280
+ S,0,143357,,0.8
+ F,0,145538,145811,1,0
+Sprite,Foreground,Centre,"SB\lyric\z\outro-en2.png",320,240
+ M,0,147720,,320,280
+ S,0,147720,,0.8
+ F,0,149902,150175,1,0
+Sprite,Foreground,Centre,"SB\lyric\ja-34-1-1.png",320,240
+ S,0,141174,,0.8
+ M,0,141174,143356,343,393
+ F,0,142265,143356,1,0
+//Storyboard Sound Samples
+//Background Colour Transformations
+3,100,163,162,255
+
+[TimingPoints]
+2629,272.727272727273,4,2,1,30,1,0
+20083,291.26213592233,4,2,1,30,1,0
+21248,288.461538461538,4,2,1,30,1,0
+22329,-181.818181818182,4,2,1,30,0,0
+22473,-181.818181818182,4,2,1,5,0,0
+23356,272.727272727273,4,2,1,5,1,0
+23492,-100,4,2,1,10,0,0
+23628,-100,4,2,1,15,0,0
+23765,-100,4,2,1,20,0,0
+23901,-100,4,2,1,25,0,0
+24037,-100,4,2,1,30,0,0
+24174,-100,4,2,1,35,0,0
+24310,-100,4,2,1,40,0,0
+24446,-117.647058823529,4,1,1,50,0,0
+25333,-100,4,1,1,5,0,0
+25537,-100,4,1,1,50,0,0
+25810,-117.647058823529,4,2,1,50,0,0
+26492,-117.647058823529,4,1,1,50,0,0
+26765,-117.647058823529,4,2,1,50,0,0
+27719,-117.647058823529,4,1,1,50,0,0
+27992,-117.647058823529,4,2,1,50,0,0
+28674,-117.647058823529,4,1,1,50,0,0
+28878,-117.647058823529,4,2,1,5,0,0
+28946,-117.647058823529,4,2,1,50,0,0
+29901,-100,4,1,1,50,0,0
+30174,-117.647058823529,4,2,1,50,0,0
+30856,-117.647058823529,4,1,1,50,0,0
+31128,-117.647058823529,4,2,1,50,0,0
+32083,-117.647058823529,4,1,1,50,0,0
+32356,-117.647058823529,4,2,1,50,0,0
+33242,-117.647058823529,4,1,1,5,0,0
+33310,-117.647058823529,4,1,1,30,0,0
+34265,-100,4,1,1,60,0,0
+42583,-117.647058823529,4,1,1,40,0,0
+42992,-100,4,1,1,60,0,0
+51719,-133.333333333333,4,2,1,60,0,0
+53492,-117.647058823529,4,2,1,80,0,0
+53628,-200,4,2,1,60,0,0
+56015,-200,4,2,1,5,0,0
+56083,-200,4,2,1,60,0,0
+60992,-200,4,2,1,60,0,0
+61060,-200,4,1,1,20,0,0
+61128,-200,4,1,1,60,0,0
+61196,-200,4,1,1,20,0,0
+61265,-200,4,1,1,60,0,0
+61401,-200,4,1,1,20,0,0
+61537,-200,4,1,1,60,0,0
+61605,-200,4,1,1,20,0,0
+61946,-200,4,1,1,60,0,0
+62015,-200,4,1,1,20,0,0
+62355,-200,4,1,1,60,0,0
+62628,-200,4,2,1,60,0,0
+70265,-100,4,1,1,60,0,0
+71356,-90.9090909090909,4,1,1,60,0,1
+78992,-90.9090909090909,4,1,1,40,0,0
+80083,-90.9090909090909,4,1,1,60,0,1
+86628,-117.647058823529,4,1,1,60,0,0
+87719,-117.647058823529,4,1,1,60,0,0
+88810,-100,4,1,1,60,0,0
+97537,-133.333333333333,4,2,1,60,0,0
+99310,-100,4,2,1,80,0,0
+99446,-200,4,2,1,60,0,0
+101833,-200,4,2,1,5,0,0
+101901,-200,4,2,1,60,0,0
+106810,-200,4,2,1,60,0,0
+106878,-200,4,2,1,20,0,0
+106878,-200,4,2,1,60,0,0
+106946,-200,4,1,1,60,0,0
+107015,-200,4,1,1,20,0,0
+107083,-200,4,1,1,60,0,0
+107219,-200,4,1,1,20,0,0
+107355,-200,4,1,1,60,0,0
+107424,-200,4,1,1,20,0,0
+107765,-200,4,1,1,60,0,0
+107833,-200,4,1,1,20,0,0
+108174,-200,4,1,1,60,0,0
+108446,-200,4,2,1,60,0,0
+116083,-200,4,1,1,60,0,0
+117174,-133.333333333333,4,1,1,60,0,1
+123719,-200,4,1,1,20,0,0
+124674,-200,4,1,1,60,0,0
+126174,-90.9090909090909,4,1,1,60,0,1
+127810,-200,4,1,1,60,0,0
+128356,-100,4,1,1,60,0,1
+129992,-200,4,2,1,60,0,0
+130265,-100,4,1,1,60,0,1
+133537,-100,4,1,1,60,0,0
+134628,-100,4,1,1,60,0,1
+141174,-100,4,1,1,60,0,0
+142265,-117.647058823529,4,1,1,60,0,1
+143287,-117.647058823529,4,1,1,5,0,1
+143355,-117.647058823529,4,1,1,5,0,0
+143356,-133.333333333333,4,2,1,60,0,0
+147651,-133.333333333333,4,2,1,5,0,0
+147719,-133.333333333333,4,2,1,60,0,0
+152083,-100,4,1,1,60,0,0
+160810,-83.3333333333333,4,1,1,60,0,1
+162992,-76.9230769230769,4,1,1,60,0,1
+165174,-71.4285714285714,4,1,1,60,0,1
+167356,-117.647058823529,4,1,1,60,0,0
+168446,-117.647058823529,4,1,1,60,0,1
+169537,-117.647058823529,4,2,1,60,0,0
+171310,-117.647058823529,4,2,1,80,0,0
+171446,-117.647058823529,4,2,1,60,0,0
+
+
+[Colours]
+Combo1 : 255,255,255
+Combo2 : 72,255,72
+Combo3 : 255,128,0
+Combo4 : 255,32,32
+Combo5 : 0,128,255
+Combo6 : 255,85,255
+Combo7 : 0,183,0
+Combo8 : 176,112,77
+
+[HitObjects]
+154,247,22401,6,0,P|56:170|169:171,1,313.500009567261
+169,170,23356,53,0,1:0:0:0:
+192,92,23492,1,0,1:0:0:0:
+252,40,23629,1,0,1:0:0:0:
+332,28,23765,1,0,1:0:0:0:
+404,60,23901,1,0,1:0:0:0:
+452,124,24038,1,0,1:0:0:0:
+456,204,24174,1,0,1:0:0:0:
+424,276,24310,1,0,1:0:0:0:
+356,316,24447,54,0,P|316:312|272:312,1,80.750001540184,0|0,0:0|2:0,0:0:0:0:
+275,311,24651,1,0,2:0:0:0:
+275,311,24719,1,2,2:0:0:0:
+168,344,24856,1,4,3:0:0:0:
+92,264,24992,1,0,0:0:0:0:
+96,249,25060,1,0,2:0:0:0:
+99,235,25129,1,0,2:0:0:0:
+103,221,25197,1,0,2:0:0:0:
+106,207,25265,54,0,P|72:144|92:104,1,121.125002310276
+108,208,25538,54,0,P|153:206|208:204,1,95
+260,88,25810,2,0,L|176:84,1,80.750001540184
+304,220,26083,1,2,0:0:0:0:
+400,212,26219,1,0,0:0:0:0:
+344,132,26356,1,0,0:0:0:0:
+356,300,26492,54,0,P|300:360|212:348,1,161.500003080368,0|0,3:0|0:0,0:0:0:0:
+200,216,26901,1,0,0:0:0:0:
+200,216,27038,1,0,0:0:0:0:
+108,328,27174,1,2,0:0:0:0:
+108,328,27310,1,0,0:0:0:0:
+28,208,27447,1,2,0:0:0:0:
+112,244,27583,1,4,3:0:0:0:
+112,244,27651,1,0,0:0:0:0:
+112,244,27719,54,0,B|104:176|88:116|88:116|104:124|120:128,1,161.500003080368
+208,164,28129,2,0,P|248:159|284:136,1,80.750001540184,0|2,0:0|0:0,0:0:0:0:
+212,72,28401,1,0,0:0:0:0:
+280,240,28538,1,0,0:0:0:0:
+388,140,28674,54,0,P|392:200|372:264,1,121.125002310276,0|0,3:0|0:0,0:0:0:0:
+375,257,28947,2,0,P|336:252|288:252,1,80.750001540184,0|2,0:0|0:0,0:0:0:0:
+456,308,29219,1,0,0:0:0:0:
+376,372,29356,1,2,0:0:0:0:
+376,372,29492,1,0,0:0:0:0:
+376,372,29629,1,2,0:0:0:0:
+376,372,29765,1,4,3:0:0:0:
+376,372,29901,54,0,P|331:371|280:368,1,95
+224,248,30174,2,0,P|264:247|312:244,1,80.750001540184
+164,352,30447,1,2,0:0:0:0:
+68,360,30583,1,0,0:0:0:0:
+108,272,30719,1,0,0:0:0:0:
+40,156,30856,54,0,P|44:80|120:36,1,161.500003080368,0|0,3:0|0:0,0:0:0:0:
+136,132,31265,1,0,0:0:0:0:
+136,132,31401,1,0,0:0:0:0:
+256,48,31538,1,2,0:0:0:0:
+256,48,31674,1,0,0:0:0:0:
+376,132,31810,1,2,0:0:0:0:
+312,224,31947,1,4,3:0:0:0:
+312,224,32015,1,0,0:0:0:0:
+312,224,32083,54,0,B|272:288|312:352|312:352|280:344,1,161.500003080368
+196,324,32492,1,0,0:0:0:0:
+312,352,32629,2,0,P|348:340|396:340,1,80.750001540184,2|0,0:0|0:0,0:0:0:0:
+484,368,32901,1,0,0:0:0:0:
+448,229,33038,54,0,B|430:168|430:168|448:149|456:113,1,121.125002310276,2|0,0:0|0:0,0:0:0:0:
+455,116,33310,2,0,L|375:132,1,80.750001540184
+208,44,33583,2,0,L|290:58,1,80.750001540184,2|0,2:0|0:0,0:0:0:0:
+295,65,34265,54,0,L|199:47,1,95,4|0,0:0|0:0,0:0:0:0:
+76,88,34538,1,8,0:0:0:0:
+116,216,34674,1,2,2:0:0:0:
+116,216,34742,1,2,2:0:0:0:
+116,216,34810,2,0,P|156:244|208:240,1,95,2|0,2:0|0:0,0:0:0:0:
+360,152,35083,2,0,P|311:147|268:177,1,95,8|0,0:0|0:0,0:0:0:0:
+336,244,35356,54,0,L|384:60,1,190,2|8,2:0|0:0,0:0:0:0:
+436,228,35765,1,2,2:0:0:0:
+287,76,35901,1,2,2:0:0:0:
+462,128,36038,1,2,2:0:0:0:
+260,176,36174,1,8,0:0:0:0:
+384,60,36310,1,2,2:0:0:0:
+360,152,36447,54,0,B|312:224|348:312|348:312|320:300,1,190,4|8,0:0|0:0,0:0:0:0:
+204,252,36856,1,2,2:0:0:0:
+204,252,36924,1,2,2:0:0:0:
+204,252,36992,2,0,P|160:236|104:244,1,95
+232,356,37265,1,8,0:0:0:0:
+176,152,37401,1,0,0:0:0:0:
+48,332,37538,54,0,L|144:324,1,95,8|2,0:0|2:0,0:0:0:0:
+300,244,37810,2,0,L|204:252,1,95,8|2,0:0|2:0,0:0:0:0:
+40,320,38083,2,0,L|136:312,1,95,0|2,0:0|2:0,0:0:0:0:
+312,252,38356,2,0,P|340:184|308:124,1,142.5,8|0,0:0|0:0,0:0:0:0:
+296,120,38629,54,0,P|340:132|390:129,1,95,4|0,0:0|0:0,0:0:0:0:
+264,44,38901,2,0,P|224:72|212:116,1,95,8|0,0:0|0:0,0:0:0:0:
+252,192,39174,1,2,2:0:0:0:
+96,120,39310,1,0,0:0:0:0:
+144,284,39447,1,8,0:0:0:0:
+256,304,39583,1,2,2:0:0:0:
+256,304,39651,1,2,2:0:0:0:
+256,304,39719,54,0,B|352:296|420:292|420:292|404:260,1,190,0|8,0:0|0:0,0:0:0:0:
+360,196,40129,1,0,0:0:0:0:
+320,376,40265,2,0,L|424:371,1,95,2|2,2:0|2:0,0:0:0:0:
+456,192,40538,2,0,L|352:197,1,95,8|0,0:0|0:0,0:0:0:0:
+416,272,40810,54,0,P|360:192|400:116,1,190,4|8,0:0|0:0,0:0:0:0:
+500,84,41219,1,0,0:0:0:0:
+380,28,41356,2,0,P|332:20|284:40,1,95,2|0,2:0|0:0,0:0:0:0:
+248,164,41629,1,8,0:0:0:0:
+120,184,41765,1,2,2:0:0:0:
+164,60,41901,54,0,L|68:56,1,95,8|2,0:0|2:0,0:0:0:0:
+25,180,42174,2,0,L|121:184,1,95,8|2,0:0|2:0,0:0:0:0:
+164,308,42447,1,8,0:0:0:0:
+419,236,42583,1,0,0:0:0:0:
+398,233,42651,1,0,0:0:0:0:
+377,231,42719,1,8,0:0:0:0:
+356,230,42788,1,0,0:0:0:0:
+335,231,42856,1,8,0:0:0:0:
+314,233,42924,1,8,0:0:0:0:
+294,237,42992,54,0,P|257:209|249:161,1,95,4|0,0:0|0:0,0:0:0:0:
+296,40,43265,1,8,0:0:0:0:
+328,168,43401,1,2,2:0:0:0:
+328,168,43469,1,2,2:0:0:0:
+328,168,43538,2,0,P|376:176|424:152,1,95,2|0,2:0|0:0,0:0:0:0:
+480,64,43810,1,8,0:0:0:0:
+492,180,43947,1,0,0:0:0:0:
+344,320,44083,54,0,P|256:328|197:233,1,190,2|8,2:0|0:0,0:0:0:0:
+294,236,44492,1,0,0:0:0:0:
+232,160,44629,53,2,2:0:0:0:
+244,28,44765,1,2,2:0:0:0:
+112,16,44901,1,8,0:0:0:0:
+96,148,45038,1,2,2:0:0:0:
+96,148,45106,1,0,0:0:0:0:
+96,148,45174,54,0,B|72:216|112:268|112:268|88:264,1,142.5,4|0,0:0|0:0,0:0:0:0:
+84,264,45447,2,0,P|36:268|0:304,1,95,8|0,0:0|0:0,0:0:0:0:
+72,344,45719,2,0,L|272:360,1,190,2|8,2:0|0:0,0:0:0:0:
+261,359,46129,1,0,0:0:0:0:
+444,296,46265,54,0,P|472:260|472:200,1,95,8|2,0:0|2:0,0:0:0:0:
+408,48,46538,2,0,P|397:92|423:146,1,95,8|2,0:0|2:0,0:0:0:0:
+384,224,46810,1,2,2:0:0:0:
+384,224,46947,2,0,L|288:216,1,95,8|8,0:0|0:0,0:0:0:0:
+320,80,47219,1,2,2:0:0:0:
+320,80,47288,1,2,2:0:0:0:
+320,80,47356,54,0,B|248:68|184:100|184:100|161:85|120:80,1,190,4|8,0:0|0:0,0:0:0:0:
+16,144,47765,1,0,0:0:0:0:
+4,284,47901,2,0,P|32:323|84:344,1,95,2|0,2:0|0:0,0:0:0:0:
+160,368,48174,1,8,0:0:0:0:
+268,348,48310,1,0,0:0:0:0:
+160,368,48447,54,0,P|206:365|256:364,1,95,2|0,2:0|0:0,0:0:0:0:
+440,308,48719,2,0,P|393:305|344:304,1,95,8|0,0:0|0:0,0:0:0:0:
+209,229,48992,1,2,2:0:0:0:
+232,224,49060,1,0,2:0:0:0:
+253,213,49129,1,2,2:0:0:0:
+270,196,49197,1,0,2:0:0:0:
+282,175,49265,1,8,0:0:0:0:
+287,151,49333,1,0,2:0:0:0:
+286,127,49401,1,2,2:0:0:0:
+279,104,49469,1,2,2:0:0:0:
+266,83,49538,54,0,P|312:97|364:86,1,95,4|0,0:0|0:0,0:0:0:0:
+195,142,49810,2,0,P|146:137|99:161,1,95,8|0,0:0|0:0,0:0:0:0:
+84,288,50083,2,0,P|188:304|264:256,1,190,2|8,2:0|0:0,0:0:0:0:
+160,224,50492,1,0,0:0:0:0:
+195,142,50629,53,8,0:0:0:0:
+172,60,50765,1,2,2:0:0:0:
+272,120,50901,53,8,0:0:0:0:
+293,37,51038,1,2,2:0:0:0:
+348,132,51174,53,8,0:0:0:0:
+407,70,51310,1,2,2:0:0:0:
+414,174,51447,53,8,0:0:0:0:
+416,179,51515,1,8,0:0:0:0:
+419,184,51583,1,8,0:0:0:0:
+421,189,51651,1,8,0:0:0:0:
+424,195,51719,38,0,P|432:231|432:267,1,71.2500027179719,0|0,1:0|0:0,0:0:0:0:
+412,356,51992,2,0,P|378:353|336:352,1,71.2500027179719,0|0,1:0|0:0,0:0:0:0:
+180,296,52265,1,0,1:0:0:0:
+180,296,52401,1,0,1:0:0:0:
+180,296,52538,2,0,P|217:294|252:293,1,71.2500027179719,0|0,1:0|0:0,0:0:0:0:
+316,216,52810,101,4,3:0:0:0:
+280,124,52947,1,0,0:0:0:0:
+280,124,53015,1,0,0:0:0:0:
+280,124,53083,1,2,0:0:0:0:
+212,196,53219,5,4,3:0:0:0:
+176,104,53356,1,0,0:0:0:0:
+176,104,53424,1,0,0:0:0:0:
+176,104,53492,1,2,0:0:0:0:
+116,176,53629,1,0,1:0:0:0:
+168,308,53765,5,2,0:0:0:0:
+168,308,53901,2,0,P|232:300|312:300,1,142.5,4|0,3:0|0:0,0:0:0:0:
+368,348,54447,2,0,P|384:312|372:256,1,95,0|0,1:0|1:0,0:0:0:0:
+320,204,54856,1,0,0:0:0:0:
+448,240,54992,54,0,L|464:184,1,47.5,4|4,3:0|3:0,3:0:0:0:
+392,152,55265,2,0,L|388:100,1,47.5,0|4,1:0|3:0,0:0:0:0:
+312,80,55538,2,0,L|284:32,1,47.5,0|4,0:0|3:0,0:0:0:0:
+228,116,55810,2,0,P|188:112|136:112,1,71.25,0|0,1:0|0:0,0:0:0:0:
+156,111,56083,54,0,P|152:152|100:192,1,95,4|0,3:0|0:0,0:0:0:0:
+36,184,56492,1,0,0:0:0:0:
+72,252,56629,1,0,1:0:0:0:
+80,120,56765,1,4,3:0:0:0:
+52,48,56901,53,0,1:0:0:0:
+114,188,57038,2,0,P|184:184|256:184,1,142.5,0|2,0:0|0:0,0:0:0:0:
+352,284,57583,1,0,0:0:0:0:
+352,284,57719,1,2,0:0:0:0:
+352,284,57856,1,0,0:0:0:0:
+352,284,57992,2,0,L|336:344,1,47.5,2|2,0:0|0:0,0:0:0:0:
+352,284,58265,54,0,P|288:277|208:276,1,142.5,4|4,3:0|3:0,0:0:0:0:
+209,275,58810,2,0,P|180:240|180:184,1,95,0|0,1:0|1:0,0:0:0:0:
+204,120,59219,1,0,1:0:0:0:
+252,212,59356,54,0,L|304:196,1,47.5,4|0,3:0|0:0,0:0:0:0:
+456,76,59629,2,0,L|404:92,1,47.5,0|4,1:0|3:0,0:0:0:0:
+260,220,59901,2,0,L|312:204,1,47.5,0|4,0:0|3:0,0:0:0:0:
+464,84,60174,2,0,L|412:100,1,47.5,0|0,1:0|0:0,0:0:0:0:
+364,152,60447,54,0,P|320:128|268:136,1,95,4|2,3:0|0:0,0:0:0:0:
+304,208,60856,1,0,0:0:0:0:
+432,289,60992,1,0,1:0:0:0:
+415,287,61060,1,0,0:0:0:0:
+398,286,61129,1,4,3:0:0:0:
+381,285,61197,1,0,0:0:0:0:
+364,286,61265,1,0,1:0:0:0:
+263,356,61401,1,0,0:0:0:0:
+263,356,61469,1,0,0:0:0:0:
+263,356,61538,53,0,0:0:0:0:
+246,352,61606,1,0,0:0:0:0:
+229,349,61674,1,0,0:0:0:0:
+212,348,61742,1,0,0:0:0:0:
+195,348,61810,1,0,0:0:0:0:
+60,362,61947,1,0,0:0:0:0:
+48,343,62015,1,0,0:0:0:0:
+39,323,62083,1,0,0:0:0:0:
+34,302,62151,1,0,0:0:0:0:
+34,280,62219,1,0,0:0:0:0:
+38,259,62288,1,0,0:0:0:0:
+46,239,62356,1,0,0:0:0:0:
+46,239,62629,54,0,B|97:232|157:220|157:220|183:242,1,142.5,4|4,3:0|3:0,0:0:0:0:
+92,156,63174,2,0,L|189:138,1,95,0|0,1:0|1:0,0:0:0:0:
+256,100,63583,1,0,1:0:0:0:
+256,176,63719,53,4,3:0:0:0:
+256,176,63856,1,4,3:0:0:0:
+256,24,63992,1,0,1:0:0:0:
+326,138,64129,2,0,L|420:156,1,95,4|4,3:0|3:0,0:0:0:0:
+476,208,64538,2,0,P|492:256|476:304,1,95,0|0,1:0|1:0,0:0:0:0:
+480,297,65083,1,0,0:0:0:0:
+328,224,65219,1,0,0:0:0:0:
+340,304,65356,53,0,1:0:0:0:
+208,324,65492,1,4,3:0:0:0:
+340,304,65629,2,0,P|288:296|260:264,1,95,0|4,1:0|3:0,1:0:0:0:
+260,188,66038,1,0,0:0:0:0:
+192,224,66174,2,0,L|136:216,1,47.5,0|4,1:0|3:0,0:0:0:0:
+24,196,66447,53,2,0:0:0:0:
+24,196,66583,1,4,3:0:0:0:
+24,196,66719,1,0,1:0:0:0:
+24,196,66856,2,0,P|0:152|8:104,1,95,4|0,3:0|0:0,0:0:0:0:
+76,140,67265,2,0,P|122:137|172:132,1,95,2|0,0:0|1:0,0:0:0:0:
+332,172,67674,1,4,3:0:0:0:
+332,172,67810,2,0,L|284:176|236:180,1,95,0|0,1:0|0:0,0:0:0:0:
+128,240,68219,1,0,0:0:0:0:
+232,276,68356,1,0,1:0:0:0:
+120,332,68492,1,4,3:0:0:0:
+224,372,68629,54,0,P|268:376|320:356,1,95,4|0,3:0|1:0,0:0:0:0:
+360,300,69038,1,4,3:0:0:0:
+432,276,69174,2,0,P|452:232|440:180,1,95,2|0,0:0|1:0,0:0:0:0:
+344,44,69583,2,0,P|340:92|372:140,1,95,4|0,3:0|0:0,0:0:0:0:
+448,88,69992,1,0,1:0:0:0:
+448,88,70265,21,0,0:0:0:0:
+419,356,70401,1,0,0:0:0:0:
+151,324,70538,1,0,0:0:0:0:
+180,56,70674,1,0,0:0:0:0:
+180,56,71219,37,0,0:0:0:0:
+256,300,71356,2,0,L|224:192,1,104.500003189087,4|0,0:0|0:0,0:0:0:0:
+202,130,71629,1,8,0:0:0:0:
+252,24,71765,53,0,0:0:0:0:
+151,324,71901,2,0,L|192:208,1,104.500003189087
+220,124,72174,1,8,0:0:0:0:
+352,56,72310,53,0,0:0:0:0:
+20,216,72447,2,0,L|124:172,1,104.500003189087
+236,116,72719,1,8,0:0:0:0:
+350,191,72856,53,0,0:0:0:0:
+468,120,72992,1,0,0:0:0:0:
+472,256,73129,1,0,0:0:0:0:
+388,120,73265,1,8,0:0:0:0:
+511,187,73401,1,0,0:0:0:0:
+392,256,73538,54,0,P|284:228|288:144,1,209.000006378174,4|8,0:0|0:0,0:0:0:0:
+168,264,73947,1,0,0:0:0:0:
+350,191,74083,2,0,P|297:189|236:188,1,104.500003189087
+468,120,74356,2,0,P|415:118|354:117,1,104.500003189087,8|0,0:0|0:0,0:0:0:0:
+172,168,74629,54,0,P|184:236|264:260,1,156.750004783631
+172,168,74901,2,0,P|119:164|60:164,1,104.500003189087,8|0,0:0|0:0,0:0:0:0:
+20,236,75174,53,0,0:0:0:0:
+80,56,75310,1,0,0:0:0:0:
+248,132,75447,2,0,P|298:125|352:124,1,104.500003189087,8|8,0:0|0:0,0:0:0:0:
+351,123,75651,1,8,0:0:0:0:
+351,123,75719,54,0,B|348:186|340:240|340:240|352:251|364:276,1,156.750004783631,12|0,0:0|0:0,0:0:0:0:
+372,288,75993,2,0,L|384:180,1,104.500003189087,8|0,0:0|0:0,0:0:0:0:
+280,316,76265,2,0,P|232:340|168:328,1,104.500003189087
+220,260,76538,1,8,0:0:0:0:
+344,368,76674,53,0,0:0:0:0:
+396,176,76810,2,0,L|384:284,1,104.500003189087
+456,348,77083,1,8,0:0:0:0:
+308,200,77219,53,0,0:0:0:0:
+472,252,77356,1,0,0:0:0:0:
+452,116,77492,1,0,0:0:0:0:
+352,24,77629,1,8,0:0:0:0:
+216,28,77765,1,0,0:0:0:0:
+112,116,77901,54,0,B|76:216|144:288|144:288|148:256,1,209.000006378174,4|8,0:0|0:0,0:0:0:0:
+196,80,78310,1,0,0:0:0:0:
+372,200,78447,2,0,B|332:164|284:148|284:148|224:156|180:184,1,209.000006378174,0|0,0:0|0:0,0:0:0:0:
+36,268,78856,1,0,0:0:0:0:
+92,368,78992,53,8,0:0:0:0:
+110,362,79060,1,0,0:0:0:0:
+128,357,79129,1,0,0:0:0:0:
+146,353,79197,1,0,0:0:0:0:
+165,350,79265,1,0,0:0:0:0:
+183,348,79333,1,0,0:0:0:0:
+201,346,79401,1,8,0:0:0:0:
+219,345,79469,1,0,0:0:0:0:
+237,345,79538,1,0,0:0:0:0:
+208,236,79674,1,0,0:0:0:0:
+370,255,79810,1,8,0:0:0:0:
+386,262,79879,1,0,0:0:0:0:
+402,268,79947,1,0,0:0:0:0:
+417,278,80015,1,0,0:0:0:0:
+431,287,80083,6,0,P|408:243|424:184,1,104.500003189087,4|0,0:0|0:0,0:0:0:0:
+486,234,80356,1,8,0:0:0:0:
+374,350,80492,1,0,0:0:0:0:
+286,215,80629,54,0,P|288:159|324:117,1,104.500003189087
+368,192,80901,1,8,0:0:0:0:
+182,243,81038,1,0,0:0:0:0:
+152,76,81174,54,0,P|168:28|221:-2,1,104.500003189087
+262,103,81447,1,8,0:0:0:0:
+105,201,81583,1,0,0:0:0:0:
+56,116,81719,53,0,0:0:0:0:
+20,248,81856,1,0,0:0:0:0:
+152,280,81992,1,8,0:0:0:0:
+192,152,82129,1,0,0:0:0:0:
+20,248,82265,54,0,P|8:320|64:380,1,156.750004783631
+60,378,82538,2,0,P|114:374|168:372,1,104.500003189087,8|0,0:0|0:0,0:0:0:0:
+260,336,82810,2,0,P|280:284|268:232,1,104.500003189087
+260,336,83083,1,8,0:0:0:0:
+192,152,83219,1,0,0:0:0:0:
+105,201,83356,54,0,P|54:203|-7:209,1,104.500003189087
+56,52,83629,2,0,P|107:54|168:60,1,104.500003189087,8|0,0:0|0:0,0:0:0:0:
+312,216,83901,2,0,P|368:184|376:96,1,156.750004783631
+360,88,84174,2,0,P|416:92|472:76,1,104.500003189087,8|0,0:0|0:0,0:0:0:0:
+380,284,84447,54,0,P|312:216|200:216,1,209.000006378174,4|8,0:0|0:0,0:0:0:0:
+256,340,84856,1,0,0:0:0:0:
+164,364,84992,2,0,P|100:356|64:328,1,104.500003189087
+16,208,85265,1,8,0:0:0:0:
+140,260,85401,53,0,0:0:0:0:
+92,132,85538,2,0,P|120:88|172:64,1,104.500003189087
+192,168,85810,1,8,0:0:0:0:
+192,168,85947,1,0,0:0:0:0:
+280,64,86083,53,0,0:0:0:0:
+404,120,86219,1,0,0:0:0:0:
+392,256,86356,1,8,0:0:0:0:
+260,284,86492,1,0,0:0:0:0:
+304,176,86629,53,0,0:0:0:0:
+288,172,87174,53,0,0:0:0:0:
+272,168,87719,69,8,0:0:0:0:
+257,174,87788,1,0,0:0:0:0:
+241,178,87856,2,0,P|202:170|160:168,1,80.750001540184
+136,284,88129,1,8,0:0:0:0:
+151,290,88197,1,0,0:0:0:0:
+167,294,88265,2,0,P|206:286|248:284,1,80.750001540184
+57,253,88538,1,8,0:0:0:0:
+57,253,88810,6,0,P|80:300|72:348,1,95,4|0,0:0|0:0,0:0:0:0:
+0,312,89083,1,8,0:0:0:0:
+148,344,89219,1,2,2:0:0:0:
+96,176,89356,2,0,P|48:168|0:192,1,95,2|0,2:0|0:0,0:0:0:0:
+160,232,89629,1,8,0:0:0:0:
+148,344,89765,1,0,0:0:0:0:
+176,144,89901,54,0,P|264:136|323:231,1,190,2|8,2:0|0:0,0:0:0:0:
+336,292,90310,1,0,0:0:0:0:
+252,260,90447,53,2,2:0:0:0:
+408,236,90583,1,2,2:0:0:0:
+244,272,90719,1,8,0:0:0:0:
+420,224,90856,1,2,2:0:0:0:
+420,224,90924,1,0,0:0:0:0:
+420,224,90992,2,0,B|452:152|400:88,1,142.5,4|0,0:0|0:0,0:0:0:0:
+401,90,91265,2,0,P|359:95|296:100,1,95,8|0,0:0|0:0,0:0:0:0:
+504,64,91538,2,0,L|304:20,1,190,2|8,2:0|0:0,0:0:0:0:
+220,80,91947,1,0,0:0:0:0:
+60,48,92083,54,0,P|32:84|32:144,1,95,8|2,0:0|2:0,0:0:0:0:
+112,292,92356,2,0,P|123:248|97:194,1,95,8|2,0:0|2:0,0:0:0:0:
+32,256,92629,1,8,0:0:0:0:
+112,116,92765,1,2,2:0:0:0:
+208,236,92901,1,8,0:0:0:0:
+192,332,93038,1,2,2:0:0:0:
+192,332,93106,1,2,2:0:0:0:
+192,332,93174,54,0,B|264:344|328:312|328:312|351:327|392:332,1,190,12|8,0:0|0:0,0:0:0:0:
+508,268,93583,1,0,0:0:0:0:
+384,184,93719,2,0,P|348:152|336:104,1,95,2|0,2:0|0:0,0:0:0:0:
+352,16,93992,1,8,0:0:0:0:
+244,36,94129,1,0,0:0:0:0:
+352,16,94265,54,0,P|306:19|256:20,1,95,2|0,2:0|0:0,0:0:0:0:
+228,184,94538,2,0,P|275:187|324:188,1,95,8|0,0:0|0:0,0:0:0:0:
+292,104,94810,53,2,2:0:0:0:
+144,116,94947,1,2,2:0:0:0:
+217,80,95083,53,8,0:0:0:0:
+120,192,95219,1,2,2:0:0:0:
+156,264,95356,54,0,P|110:250|58:261,1,95,4|0,0:0|0:0,0:0:0:0:
+124,352,95629,2,0,P|173:357|220:333,1,95,8|0,0:0|0:0,0:0:0:0:
+412,228,95901,2,0,P|308:212|232:260,1,190,2|8,2:0|0:0,0:0:0:0:
+340,308,96310,1,0,0:0:0:0:
+389,146,96447,37,8,0:0:0:0:
+412,228,96583,1,2,2:0:0:0:
+300,124,96719,101,8,0:0:0:0:
+323,206,96856,1,2,2:0:0:0:
+212,104,96992,37,8,0:0:0:0:
+235,186,97129,1,2,2:0:0:0:
+148,304,97265,53,8,0:0:0:0:
+148,304,97333,1,8,0:0:0:0:
+148,304,97401,1,8,0:0:0:0:
+148,304,97469,1,8,0:0:0:0:
+148,304,97538,86,0,P|112:296|72:288,1,71.2500027179719,0|0,1:0|0:0,0:0:0:0:
+28,184,97810,2,0,P|65:177|107:173,1,71.2500027179719,0|0,1:0|0:0,1:0:0:0:
+232,152,98083,1,0,1:0:0:0:
+232,152,98219,1,0,1:0:0:0:
+232,152,98356,2,0,P|222:185|217:223,1,71.2500027179719,0|0,1:0|0:0,0:0:0:0:
+248,352,98629,101,4,3:0:0:0:
+328,324,98765,1,0,0:0:0:0:
+328,324,98833,1,0,0:0:0:0:
+328,324,98901,1,2,0:0:0:0:
+476,288,99038,5,4,3:0:0:0:
+392,268,99174,1,0,0:0:0:0:
+392,268,99242,1,0,0:0:0:0:
+392,268,99310,1,2,0:0:0:0:
+448,212,99447,1,0,1:0:0:0:
+264,272,99583,5,2,0:0:0:0:
+264,272,99719,2,0,P|256:208|312:160,1,142.5,4|0,3:0|0:0,0:0:0:0:
+392,156,100265,2,0,P|381:118|335:82,1,95,0|0,1:0|1:0,0:0:0:0:
+264,96,100674,1,0,0:0:0:0:
+192,72,100810,54,0,L|136:56,1,47.5,4|0,3:0|0:0,0:0:0:0:
+32,96,101083,2,0,L|88:112,1,47.5,0|4,1:0|3:0,0:0:0:0:
+168,192,101356,2,0,L|112:176,1,47.5,0|4,0:0|3:0,0:0:0:0:
+60,228,101629,2,0,P|56:268|56:320,1,71.25,0|0,1:0|0:0,0:0:0:0:
+55,299,101901,54,0,P|100:280|144:288,1,95,4|2,3:0|0:0,0:0:0:0:
+108,356,102310,1,0,0:0:0:0:
+184,356,102447,1,0,1:0:0:0:
+188,224,102583,1,4,3:0:0:0:
+224,288,102719,53,0,1:0:0:0:
+117,193,102856,2,0,P|173:149|245:173,1,142.5,4|8,3:0|1:0,0:0:0:0:
+308,304,103401,1,4,3:0:0:0:
+308,304,103538,1,0,1:0:0:0:
+308,304,103674,1,4,3:0:0:0:
+308,304,103810,2,0,L|324:244,1,47.5,0|0,1:0|0:0,0:0:0:0:
+308,304,104083,54,0,P|340:352|424:336,1,142.5,4|4,3:0|3:0,0:0:0:0:
+456,280,104629,2,0,P|452:231|448:180,1,95,0|0,1:0|1:0,0:0:0:0:
+440,108,105038,1,2,0:0:0:0:
+340,44,105174,54,0,L|288:60,1,47.5,4|0,3:0|0:0,0:0:0:0:
+172,32,105447,2,0,L|188:84,1,47.5,0|4,1:0|3:0,0:0:0:0:
+164,192,105719,2,0,L|216:176,1,47.5,0|4,0:0|3:0,0:0:0:0:
+324,208,105992,2,0,L|308:156,1,47.5,0|0,1:0|0:0,0:0:0:0:
+252,112,106265,54,0,P|208:88|156:96,1,95,4|2,3:0|0:0,0:0:0:0:
+192,164,106674,1,0,0:0:0:0:
+48,240,106810,1,0,1:0:0:0:
+65,242,106879,1,0,0:0:0:0:
+82,243,106947,1,4,3:0:0:0:
+99,244,107015,1,0,0:0:0:0:
+116,243,107083,1,0,1:0:0:0:
+180,324,107219,1,0,0:0:0:0:
+180,324,107288,1,0,0:0:0:0:
+180,324,107356,53,0,0:0:0:0:
+197,320,107424,1,0,0:0:0:0:
+214,317,107492,1,0,0:0:0:0:
+231,316,107560,1,0,0:0:0:0:
+248,316,107629,1,0,0:0:0:0:
+364,367,107765,1,0,0:0:0:0:
+372,347,107833,1,0,0:0:0:0:
+376,326,107901,1,0,0:0:0:0:
+376,304,107969,1,0,0:0:0:0:
+371,283,108038,1,0,0:0:0:0:
+362,263,108106,1,0,0:0:0:0:
+350,244,108174,1,0,0:0:0:0:
+350,244,108447,54,0,B|361:178|372:120|372:120|344:132,1,142.5,4|4,3:0|3:0,0:0:0:0:
+268,256,108992,2,0,L|286:157,1,95,0|0,1:0|1:0,0:0:0:0:
+256,92,109401,1,4,3:0:0:0:
+208,152,109538,53,4,3:0:0:0:
+208,152,109674,1,0,0:0:0:0:
+88,76,109810,1,0,1:0:0:0:
+84,216,109947,2,0,P|36:212|-4:180,1,95,4|4,3:0|3:0,0:0:0:0:
+64,144,110356,1,0,1:0:0:0:
+24,320,110629,54,0,P|72:321|124:324,1,95,4|2,3:0|0:0,0:0:0:0:
+196,348,111038,1,0,0:0:0:0:
+184,272,111174,53,0,1:0:0:0:
+316,252,111310,1,4,3:0:0:0:
+184,272,111447,2,0,P|236:280|264:312,1,95,0|4,1:0|3:0,0:0:0:0:
+292,380,111856,1,0,0:0:0:0:
+344,324,111992,2,0,L|400:332,1,47.5,0|4,1:0|3:0,0:0:0:0:
+488,248,112265,53,2,0:0:0:0:
+488,248,112401,1,4,3:0:0:0:
+488,248,112538,1,0,1:0:0:0:
+488,248,112674,2,0,P|512:204|504:156,1,95,4|0,3:0|0:0,0:0:0:0:
+456,104,113083,2,0,P|410:107|360:112,1,95,2|0,0:0|1:0,0:0:0:0:
+308,232,113492,1,4,3:0:0:0:
+308,232,113629,2,0,P|268:204|212:212,1,95,0|4,1:0|3:0,0:0:0:0:
+248,280,114038,1,0,0:0:0:0:
+128,348,114174,1,0,1:0:0:0:
+148,220,114310,1,4,3:0:0:0:
+76,184,114447,54,0,P|108:150|163:141,1,95,4|0,3:0|1:0,0:0:0:0:
+228,164,114856,1,0,0:0:0:0:
+288,116,114992,2,0,P|336:104|384:112,1,95,4|0,3:0|1:0,0:0:0:0:
+468,244,115401,2,0,P|468:196|440:148,1,95,4|0,3:0|0:0,0:0:0:0:
+376,188,115810,1,0,1:0:0:0:
+376,188,116083,85,0,0:0:0:0:
+248,324,116219,1,0,0:0:0:0:
+164,156,116356,1,0,0:0:0:0:
+288,16,116492,1,0,0:0:0:0:
+288,16,117038,53,0,0:0:0:0:
+196,56,117174,2,0,P|124:48|48:48,1,142.500005435944,4|0,0:0|0:0,0:0:0:0:
+16,140,117583,1,0,0:0:0:0:
+72,232,117719,2,0,P|116:256|180:248,1,106.875004076958,8|0,0:0|0:0,0:0:0:0:
+184,248,117992,2,0,P|221:249|256:264,1,71.2500027179719,0|0,0:0|0:0,0:0:0:0:
+300,340,118265,6,0,P|372:356|440:320,1,142.500005435944
+448,240,118674,1,0,0:0:0:0:
+408,160,118810,101,8,0:0:0:0:
+464,92,118947,1,0,0:0:0:0:
+324,144,119083,5,0,0:0:0:0:
+408,160,119219,1,0,0:0:0:0:
+256,192,119356,102,0,P|188:196|112:200,1,142.500005435944
+56,120,119765,1,0,0:0:0:0:
+72,288,119901,2,0,P|126:282|184:300,1,106.875004076958,8|0,0:0|0:0,0:0:0:0:
+192,304,120174,2,0,P|230:306|264:296,1,71.2500027179719
+352,240,120447,6,0,P|416:208|488:228,1,142.500005435944
+428,308,120856,1,0,0:0:0:0:
+460,128,120992,101,8,0:0:0:0:
+424,36,121129,1,0,0:0:0:0:
+360,112,121265,1,0,0:0:0:0:
+296,72,121401,1,0,0:0:0:0:
+296,72,121469,1,0,0:0:0:0:
+296,72,121538,6,0,P|248:96|180:88,1,106.875004076958,4|0,0:0|0:0,0:0:0:0:
+194,19,121810,2,0,P|228:5|268:6,1,71.2500027179719
+136,148,122083,102,0,P|106:192|43:219,1,106.875004076958,8|0,0:0|0:0,0:0:0:0:
+23,153,122356,2,0,P|45:123|76:103,1,71.2500027179719
+184,256,122629,6,0,P|236:254|284:278,1,106.875004076958
+253,347,122901,2,0,P|218:343|184:329,1,71.2500027179719
+240,164,123174,101,8,0:0:0:0:
+204,88,123310,1,0,0:0:0:0:
+324,180,123447,5,0,0:0:0:0:
+240,164,123583,1,0,0:0:0:0:
+292,100,123719,6,0,P|392:132|328:264,1,285,4|0,3:0|0:0,0:0:0:0:
+324,180,124674,101,0,0:0:0:0:
+324,180,124810,1,0,0:0:0:0:
+248,264,124947,1,0,0:0:0:0:
+216,152,125083,1,0,0:0:0:0:
+136,232,125219,1,0,0:0:0:0:
+104,124,125356,1,0,0:0:0:0:
+24,200,125492,1,0,0:0:0:0:
+72,352,125629,37,0,0:0:0:0:
+72,352,125765,1,0,0:0:0:0:
+72,352,125901,2,0,P|108:346|152:344,1,71.25,2|0,2:0|0:0,0:0:0:0:
+156,324,126174,6,0,P|108:340|48:332,1,104.500003189087
+248,264,126447,2,0,P|300:258|348:274,1,104.500003189087,0|0,0:0|0:0,0:0:0:0:
+432,368,126719,1,8,0:0:0:0:
+464,248,126856,53,0,0:0:0:0:
+292,340,126992,2,0,L|308:184,1,156.750004783631,0|0,0:0|0:0,0:0:0:0:
+312,164,127265,2,0,P|300:108|264:76,1,104.500003189087,8|0,0:0|0:0,0:0:0:0:
+220,156,127538,1,0,0:0:0:0:
+368,76,127674,1,0,0:0:0:0:
+200,152,127810,1,8,0:0:0:0:
+388,80,127947,53,8,0:0:0:0:
+400,96,128015,1,8,0:0:0:0:
+412,112,128083,1,8,0:0:0:0:
+412,112,128356,54,0,P|348:144|288:96,1,142.5,12|0,0:0|0:0,0:0:0:0:
+284,92,128629,2,0,P|238:93|188:88,1,95
+24,176,128901,2,0,P|73:173|120:172,1,95,8|0,0:0|0:0,0:0:0:0:
+200,244,129174,53,0,0:0:0:0:
+152,384,129310,1,0,0:0:0:0:
+304,356,129447,1,8,0:0:0:0:
+220,324,129583,1,0,0:0:0:0:
+268,188,129719,1,8,0:0:0:0:
+288,184,129788,1,0,0:0:0:0:
+308,184,129856,1,0,0:0:0:0:
+325,188,129924,1,0,0:0:0:0:
+342,195,129992,1,2,0:0:0:0:
+342,195,130265,54,0,P|320:152|324:100,1,95,4|0,0:0|0:0,0:0:0:0:
+428,268,130538,1,8,0:0:0:0:
+312,304,130674,1,0,0:0:0:0:
+408,92,130810,54,0,P|368:32|284:28,1,142.5,0|0,0:0|0:0,0:0:0:0:
+280,32,131083,2,0,P|233:27|180:32,1,95,8|0,0:0|0:0,0:0:0:0:
+164,42,131288,1,0,0:0:0:0:
+141,54,131356,54,0,B|150:142|150:142|132:163|124:192,1,142.5
+120,204,131629,2,0,P|168:180|208:180,1,95,8|0,0:0|0:0,0:0:0:0:
+308,236,131901,53,0,0:0:0:0:
+136,320,132038,1,0,0:0:0:0:
+228,152,132174,1,8,0:0:0:0:
+256,344,132310,1,2,2:0:0:0:
+256,344,132379,1,2,2:0:0:0:
+256,344,132447,54,0,B|305:335|352:332|352:332|372:280|424:260,1,190,4|8,0:0|0:0,0:0:0:0:
+300,256,132856,2,0,P|255:261|204:268,1,95
+120,204,133129,1,0,0:0:0:0:
+212,164,133265,53,8,0:0:0:0:
+164,20,133401,1,0,0:0:0:0:
+312,48,133538,54,0,L|344:88,1,47.5,8|0,0:0|0:0,0:0:0:0:
+504,120,133674,2,0,L|453:127,1,47.5
+344,252,133810,2,0,L|362:204,1,47.5
+428,32,133947,2,0,L|409:79,1,47.5,8|0,0:0|0:0,0:0:0:0:
+461,234,134083,2,0,L|429:194,1,47.5
+267,161,134219,2,0,L|317:153,1,47.5
+388,140,134356,1,8,0:0:0:0:
+394,142,134424,1,0,0:0:0:0:
+399,145,134492,1,0,0:0:0:0:
+405,148,134560,1,0,0:0:0:0:
+411,152,134629,102,0,P|439:208|407:280,1,142.5,4|0,0:0|0:0,0:0:0:0:
+372,340,134901,2,0,P|324:341|276:344,1,95,8|0,0:0|0:0,0:0:0:0:
+56,308,135174,53,0,0:0:0:0:
+200,208,135310,1,8,0:0:0:0:
+204,384,135447,1,0,0:0:0:0:
+276,244,135583,53,0,0:0:0:0:
+128,348,135719,1,0,0:0:0:0:
+128,168,135856,2,0,P|168:128|212:124,1,95,0|8,0:0|0:0,0:0:0:0:
+20,228,136129,53,0,0:0:0:0:
+200,208,136265,1,0,0:0:0:0:
+8,128,136401,1,8,0:0:0:0:
+186,108,136538,1,0,0:0:0:0:
+116,274,136674,1,0,0:0:0:0:
+92,64,136810,54,0,B|99:117|112:160|112:160|104:180|96:208,1,142.5,4|0,0:0|0:0,0:0:0:0:
+116,274,137083,2,0,P|166:268|212:268,1,95,8|0,0:0|0:0,0:0:0:0:
+372,352,137356,1,0,0:0:0:0:
+210,267,137492,1,0,0:0:0:0:
+372,352,137629,2,0,P|420:340|452:304,1,95,8|0,0:0|0:0,0:0:0:0:
+424,216,137901,53,0,0:0:0:0:
+448,56,138038,1,0,0:0:0:0:
+352,160,138174,5,8,0:0:0:0:
+280,16,138310,1,0,0:0:0:0:
+264,152,138447,5,0,0:0:0:0:
+120,76,138583,1,0,0:0:0:0:
+180,188,138719,5,8,0:0:0:0:
+24,224,138856,1,0,0:0:0:0:
+136,268,138992,70,0,P|184:268|232:272,1,95,4|0,0:0|0:0,0:0:0:0:
+332,284,139265,1,8,0:0:0:0:
+272,364,139401,53,0,0:0:0:0:
+292,188,139538,2,0,P|332:124|408:120,1,142.5
+376,200,139810,2,0,P|425:195|480:188,1,95,8|0,0:0|0:0,0:0:0:0:
+368,372,140083,54,0,P|320:368|268:364,1,95
+168,352,140356,1,8,0:0:0:0:
+232,276,140492,53,0,0:0:0:0:
+220,152,140629,1,0,0:0:0:0:
+104,108,140765,1,0,0:0:0:0:
+8,188,140901,1,8,0:0:0:0:
+4,212,140969,1,0,0:0:0:0:
+8,236,141038,1,0,0:0:0:0:
+20,256,141106,1,0,0:0:0:0:
+38,272,141174,53,0,0:0:0:0:
+77,284,141719,53,0,0:0:0:0:
+116,292,142265,54,0,B|104:196|8:232|-44:140|4:44|60:20|136:16|172:76|172:76|152:96|152:96|163:120|163:120|144:141|144:141|155:165|155:165|136:188|136:188|156:204|172:236|172:236,1,605.62501155138
+168,224,143356,6,0,P|228:188|304:196,1,142.500005435944,0|2,1:0|0:0,0:0:0:0:
+298,193,143697,1,0,0:0:0:0:
+298,193,143765,1,0,0:0:0:0:
+352,264,143901,1,0,0:0:0:0:
+424,212,144038,2,0,P|472:152|464:88,2,142.500005435944,2|2|0,0:0|0:0|0:0,0:0:0:0:
+388,132,144719,54,0,L|364:48,1,71.2500027179719,0|0,0:0|0:0,0:0:0:0:
+296,112,144992,2,0,P|264:96|224:96,2,71.2500027179719,2|0|0,0:0|0:0|0:0,0:0:0:0:
+240,176,145401,53,0,0:0:0:0:
+344,308,145538,2,0,P|288:348|216:344,1,142.500005435944,2|0,0:0|0:0,0:0:0:0:
+260,260,145947,1,2,0:0:0:0:
+172,240,146083,53,0,0:0:0:0:
+172,240,146219,1,0,0:0:0:0:
+172,240,146356,1,0,0:0:0:0:
+172,240,146492,2,0,B|150:244|128:252|128:252|86:238|32:260,1,142.500005435944,2|0,0:0|0:0,0:0:0:0:
+124,168,146901,2,0,B|146:164|168:156|168:156|210:170|264:148,1,142.500005435944,2|2,0:0|0:0,0:0:0:0:
+276,140,147242,1,0,0:0:0:0:
+288,132,147310,1,0,0:0:0:0:
+448,180,147447,2,0,B|403:182|360:176|360:176|372:192,1,106.875004076958,2|0,0:0|0:0,0:0:0:0:
+380,204,147719,54,0,P|420:264|384:336,1,142.500005435944,4|2,0:0|0:0,0:0:0:0:
+397,325,148129,1,0,0:0:0:0:
+332,268,148265,1,0,0:0:0:0:
+276,332,148401,2,0,P|203:327|128:328,2,142.500005435944,2|2|0,0:0|0:0|0:0,0:0:0:0:
+48,264,149083,1,0,0:0:0:0:
+48,264,149219,2,0,P|28:196|60:136,2,142.500005435944,2|2|0,0:0|0:0|0:0,0:0:0:0:
+112,208,149901,53,2,0:0:0:0:
+296,256,150174,1,2,0:0:0:0:
+296,256,150242,1,0,0:0:0:0:
+296,256,150310,2,0,P|336:252|368:232,1,71.2500027179719,0|0,0:0|0:0,0:0:0:0:
+388,72,150583,2,0,P|348:76|316:96,1,71.2500027179719,2|0,0:0|0:0,0:0:0:0:
+308,172,150856,2,0,L|460:208,1,142.500005435944,2|0,0:0|0:0,0:0:0:0:
+446,204,151265,37,2,0:0:0:0:
+446,204,151401,1,0,0:0:0:0:
+446,204,151538,5,2,0:0:0:0:
+446,204,152083,86,0,P|472:248|464:304,1,95,4|0,0:0|0:0,0:0:0:0:
+400,384,152356,1,8,0:0:0:0:
+392,264,152492,1,2,2:0:0:0:
+392,264,152560,1,2,2:0:0:0:
+392,264,152629,2,0,P|348:240|300:240,1,95,2|0,2:0|0:0,0:0:0:0:
+328,320,152901,1,8,0:0:0:0:
+280,160,153038,1,0,0:0:0:0:
+328,320,153174,54,0,L|140:344,1,190,2|8,2:0|0:0,0:0:0:0:
+64,296,153583,1,0,0:0:0:0:
+176,236,153719,2,2,L|224:244,2,47.5,2|2|2,2:0|2:0|2:0,2:0:0:0:
+4,184,153992,1,8,0:0:0:0:
+116,124,154129,1,2,2:0:0:0:
+116,124,154197,1,2,2:0:0:0:
+116,124,154265,54,0,P|136:68|128:32,1,95,4|0,0:0|0:0,0:0:0:0:
+16,96,154538,2,0,P|-4:152|4:188,1,95,8|0,0:0|0:0,0:0:0:0:
+76,288,154810,2,0,P|124:284|176:284,1,95,2|0,2:0|0:0,0:0:0:0:
+292,340,155083,1,8,0:0:0:0:
+276,208,155219,1,0,0:0:0:0:
+380,312,155356,54,0,P|430:304|488:300,1,95,8|2,0:0|2:0,0:0:0:0:
+384,232,155629,2,0,P|434:224|492:220,1,95,8|2,0:0|2:0,0:0:0:0:
+312,128,155901,2,0,P|262:120|204:116,1,95,8|2,0:0|2:0,0:0:0:0:
+308,48,156174,2,0,P|258:40|200:36,1,95,0|2,0:0|2:0,0:0:0:0:
+213,36,156379,1,2,2:0:0:0:
+213,36,156447,54,0,P|308:35|408:32,1,190,4|8,0:0|0:0,0:0:0:0:
+480,88,156856,1,0,0:0:0:0:
+384,232,156992,2,0,P|288:236|236:164,1,190,2|8,2:0|0:0,0:0:0:0:
+328,152,157401,1,0,0:0:0:0:
+264,324,157538,2,0,P|168:324|64:332,1,190,2|8,2:0|0:0,0:0:0:0:
+112,248,157947,1,2,2:0:0:0:
+112,248,158015,1,2,2:0:0:0:
+112,248,158083,53,2,2:0:0:0:
+28,144,158219,1,2,2:0:0:0:
+100,32,158356,1,8,0:0:0:0:
+228,72,158492,1,2,2:0:0:0:
+232,204,158629,54,0,P|264:244|320:256,1,95,4|0,0:0|0:0,0:0:0:0:
+484,208,158901,2,0,P|436:189|381:206,1,95,8|0,0:0|0:0,0:0:0:0:
+160,328,159174,2,0,P|164:256|216:208,1,142.5,2|2,2:0|2:0,0:0:0:0:
+232,204,159447,2,0,P|284:208|320:236,1,95,8|0,0:0|0:0,0:0:0:0:
+251,295,159719,54,0,L|215:103,1,190,8|8,0:0|0:0,0:0:0:0:
+296,64,160129,1,2,2:0:0:0:
+128,152,160265,1,2,2:0:0:0:
+105,144,160333,1,2,2:0:0:0:
+84,131,160401,1,2,2:0:0:0:
+67,114,160469,1,0,2:0:0:0:
+54,94,160538,1,8,0:0:0:0:
+52,117,160606,1,0,2:0:0:0:
+48,140,160674,1,2,2:0:0:0:
+37,161,160742,1,2,2:0:0:0:
+23,179,160810,6,0,P|5:234|16:292,1,113.999996520996,4|8,0:0|0:0,0:0:0:0:
+92,240,161083,2,0,P|103:296|142:341,1,113.999996520996,0|8,0:0|0:0,0:0:0:0:
+184,264,161356,2,0,P|222:307|278:326,1,113.999996520996,0|8,0:0|0:0,0:0:0:0:
+276,240,161629,2,0,P|331:258|388:247,1,113.999996520996,0|8,0:0|0:0,0:0:0:0:
+500,184,161901,54,0,P|518:129|507:71,1,113.999996520996,0|8,0:0|0:0,0:0:0:0:
+403,107,162174,2,0,P|391:50|352:5,1,113.999996520996,0|8,0:0|0:0,0:0:0:0:
+275,87,162447,2,0,P|236:43|180:24,1,113.999996520996,8|0,0:0|0:0,0:0:0:0:
+151,127,162719,2,0,P|96:109|38:120,1,113.999996520996,8|0,0:0|0:0,0:0:0:0:
+84,292,162992,53,4,0:0:0:0:
+188,228,163129,1,8,0:0:0:0:
+200,352,163265,2,0,P|260:344|332:344,1,123.499994346619,0|8,0:0|0:0,0:0:0:0:
+512,200,163538,53,0,0:0:0:0:
+408,136,163674,1,8,0:0:0:0:
+396,260,163810,2,0,P|336:252|264:252,1,123.499994346619,0|8,0:0|0:0,0:0:0:0:
+240,80,164083,54,0,P|192:32|128:32,1,123.499994346619,0|8,0:0|0:0,0:0:0:0:
+64,176,164356,2,0,P|112:224|176:224,1,123.499994346619,0|8,0:0|0:0,0:0:0:0:
+244,72,164629,54,0,P|196:24|132:24,1,123.499994346619,0|8,0:0|0:0,0:0:0:0:
+56,184,164901,2,8,P|104:232|168:232,1,123.499994346619,8|8,0:0|0:0,0:0:0:0:
+328,128,165174,54,0,P|392:120|448:76,1,132.999995941162,4|8,0:0|0:0,0:0:0:0:
+360,212,165447,2,0,B|394:206|416:200|416:200|456:216|500:208,1,132.999995941162,0|8,0:0|0:0,0:0:0:0:
+328,300,165719,2,0,P|392:320|452:352,1,132.999995941162,0|8,0:0|0:0,0:0:0:0:
+184,300,165992,2,0,P|120:320|60:352,1,132.999995941162,0|8,0:0|0:0,0:0:0:0:
+152,212,166265,2,0,B|117:206|96:200|96:200|56:216|12:208,1,132.999995941162,0|8,0:0|0:0,0:0:0:0:
+184,128,166538,2,0,P|120:120|64:76,1,132.999995941162,0|8,0:0|0:0,0:0:0:0:
+256,80,166810,53,8,0:0:0:0:
+256,100,166879,1,0,2:0:0:0:
+256,120,166947,1,2,2:0:0:0:
+256,140,167015,1,2,2:0:0:0:
+256,160,167083,1,2,2:0:0:0:
+256,256,167219,1,2,2:0:0:0:
+256,256,167288,1,0,2:0:0:0:
+256,256,167356,53,4,3:0:0:0:
+256,276,167901,53,0,0:0:0:0:
+256,296,168447,53,0,0:0:0:0:
+256,84,168583,1,8,0:0:0:0:
+181,265,168719,1,0,0:0:0:0:
+330,115,168856,1,8,0:0:0:0:
+150,190,168992,1,0,0:0:0:0:
+361,190,169129,1,8,0:0:0:0:
+181,115,169265,1,0,0:0:0:0:
+330,265,169401,1,8,0:0:0:0:
+256,192,169538,38,0,L|344:184,1,80.750001540184,4|0,3:0|0:0,0:0:0:0:
+256,192,169810,2,0,L|168:200,1,80.750001540184,4|0,3:0|0:0,0:0:0:0:
+256,36,170083,1,4,3:0:0:0:
+256,36,170219,1,2,0:0:0:0:
+256,36,170356,2,0,L|256:116,1,80.750001540184
+208,260,170629,69,2,0:0:0:0:
+191,249,170697,1,0,0:0:0:0:
+176,240,170765,1,0,0:0:0:0:
+256,192,170901,1,0,0:0:0:0:
+304,260,171038,37,2,0:0:0:0:
+321,249,171106,1,0,0:0:0:0:
+336,240,171174,1,0,0:0:0:0:
+256,192,171310,1,0,0:0:0:0:
+256,332,171447,101,4,3:0:0:0:
diff --git a/osu.Game.Tests/Visual/TestCaseStoryboard.cs b/osu.Game.Tests/Visual/TestCaseStoryboard.cs
index 1dad106cbe..0a158f5662 100644
--- a/osu.Game.Tests/Visual/TestCaseStoryboard.cs
+++ b/osu.Game.Tests/Visual/TestCaseStoryboard.cs
@@ -79,7 +79,7 @@ namespace osu.Game.Tests.Visual
var decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = true };
storyboardContainer.Clock = decoupledClock;
- storyboard = working.Beatmap.Storyboard.CreateDrawable(beatmapBacking);
+ storyboard = working.Storyboard.CreateDrawable(beatmapBacking);
storyboard.Passing = false;
storyboardContainer.Add(storyboard);
diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj
index b8dce4e4f6..b7536112e3 100644
--- a/osu.Game.Tests/osu.Game.Tests.csproj
+++ b/osu.Game.Tests/osu.Game.Tests.csproj
@@ -1,5 +1,6 @@
-
+
+
Debug
AnyCPU
@@ -19,7 +20,6 @@
4
false
false
- 6
true
@@ -45,9 +45,6 @@
-
- osu.licenseheader
-
@@ -86,10 +83,11 @@
+
-
+
@@ -150,6 +148,7 @@
+
\ No newline at end of file
diff --git a/osu.Game.props b/osu.Game.props
new file mode 100644
index 0000000000..60a5e97944
--- /dev/null
+++ b/osu.Game.props
@@ -0,0 +1,13 @@
+
+
+
+
+
+ 7
+
+
+
+ osu.licenseheader
+
+
+
\ No newline at end of file
diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs
index 35b6cc2b02..c8390310d4 100644
--- a/osu.Game/Beatmaps/Beatmap.cs
+++ b/osu.Game/Beatmaps/Beatmap.cs
@@ -8,7 +8,6 @@ using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.IO.Serialization;
-using osu.Game.Storyboards;
namespace osu.Game.Beatmaps
{
@@ -41,11 +40,6 @@ namespace osu.Game.Beatmaps
///
public double TotalBreakTime => Breaks.Sum(b => b.Duration);
- ///
- /// The Beatmap's Storyboard.
- ///
- public Storyboard Storyboard = new Storyboard();
-
///
/// Constructs a new beatmap.
///
@@ -57,7 +51,6 @@ namespace osu.Game.Beatmaps
Breaks = original?.Breaks ?? Breaks;
ComboColors = original?.ComboColors ?? ComboColors;
HitObjects = original?.HitObjects ?? HitObjects;
- Storyboard = original?.Storyboard ?? Storyboard;
if (original == null && Metadata == null)
{
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 0641cabcd8..edbda1a685 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -25,6 +25,7 @@ using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets;
+using osu.Game.Storyboards;
namespace osu.Game.Beatmaps
{
@@ -494,7 +495,7 @@ namespace osu.Game.Beatmaps
BeatmapMetadata metadata;
using (var stream = new StreamReader(reader.GetStream(mapName)))
- metadata = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata;
+ metadata = Decoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata;
// check if a set already exists with the same online id.
beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.OnlineBeatmapSetID == metadata.OnlineBeatmapSetID) ?? new BeatmapSetInfo
@@ -517,8 +518,8 @@ namespace osu.Game.Beatmaps
raw.CopyTo(ms);
ms.Position = 0;
- var decoder = BeatmapDecoder.GetDecoder(sr);
- Beatmap beatmap = decoder.Decode(sr);
+ var decoder = Decoder.GetDecoder(sr);
+ Beatmap beatmap = decoder.DecodeBeatmap(sr);
beatmap.BeatmapInfo.Path = name;
beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
@@ -568,23 +569,11 @@ namespace osu.Game.Beatmaps
{
try
{
- Beatmap beatmap;
-
- BeatmapDecoder decoder;
using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path))))
{
- decoder = BeatmapDecoder.GetDecoder(stream);
- beatmap = decoder.Decode(stream);
+ Decoder decoder = Decoder.GetDecoder(stream);
+ return decoder.DecodeBeatmap(stream);
}
-
- if (beatmap == null || BeatmapSetInfo.StoryboardFile == null)
- return beatmap;
-
- using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile))))
- decoder.Decode(stream, beatmap);
-
-
- return beatmap;
}
catch
{
@@ -623,6 +612,28 @@ namespace osu.Game.Beatmaps
}
protected override Waveform GetWaveform() => new Waveform(store.GetStream(getPathForFile(Metadata.AudioFile)));
+
+ protected override Storyboard GetStoryboard()
+ {
+ if (BeatmapInfo?.Path == null && BeatmapSetInfo?.StoryboardFile == null)
+ return new Storyboard();
+
+ try
+ {
+ Decoder decoder;
+ using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo?.Path))))
+ decoder = Decoder.GetDecoder(stream);
+
+ // try for .osb first and fall back to .osu
+ string storyboardFile = BeatmapSetInfo.StoryboardFile ?? BeatmapInfo.Path;
+ using (var stream = new StreamReader(store.GetStream(getPathForFile(storyboardFile))))
+ return decoder.GetStoryboardDecoder().DecodeStoryboard(stream);
+ }
+ catch
+ {
+ return new Storyboard();
+ }
+ }
}
///
diff --git a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs
deleted file mode 100644
index 7e1a87085c..0000000000
--- a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-
-namespace osu.Game.Beatmaps.Formats
-{
- public abstract class BeatmapDecoder
- {
- private static readonly Dictionary decoders = new Dictionary();
-
- static BeatmapDecoder()
- {
- OsuLegacyDecoder.Register();
- }
-
- public static BeatmapDecoder GetDecoder(StreamReader stream)
- {
- if (stream == null)
- throw new ArgumentNullException(nameof(stream));
-
- string line;
- do { line = stream.ReadLine()?.Trim(); }
- while (line != null && line.Length == 0);
-
- if (line == null || !decoders.ContainsKey(line))
- throw new IOException(@"Unknown file format");
- return (BeatmapDecoder)Activator.CreateInstance(decoders[line], line);
- }
-
- protected static void AddDecoder(string magic) where T : BeatmapDecoder
- {
- decoders[magic] = typeof(T);
- }
-
- public virtual Beatmap Decode(StreamReader stream)
- {
- return ParseFile(stream);
- }
-
- public virtual void Decode(StreamReader stream, Beatmap beatmap)
- {
- ParseFile(stream, beatmap);
- }
-
- protected virtual Beatmap ParseFile(StreamReader stream)
- {
- var beatmap = new Beatmap
- {
- BeatmapInfo = new BeatmapInfo
- {
- Metadata = new BeatmapMetadata(),
- BaseDifficulty = new BeatmapDifficulty(),
- },
- };
-
- ParseFile(stream, beatmap);
- return beatmap;
- }
-
- protected abstract void ParseFile(StreamReader stream, Beatmap beatmap);
- }
-}
diff --git a/osu.Game/Beatmaps/Formats/Decoder.cs b/osu.Game/Beatmaps/Formats/Decoder.cs
new file mode 100644
index 0000000000..e157150651
--- /dev/null
+++ b/osu.Game/Beatmaps/Formats/Decoder.cs
@@ -0,0 +1,80 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using osu.Game.Storyboards;
+
+namespace osu.Game.Beatmaps.Formats
+{
+ public abstract class Decoder
+ {
+ private static readonly Dictionary decoders = new Dictionary();
+
+ static Decoder()
+ {
+ LegacyDecoder.Register();
+ }
+
+ ///
+ /// Retrieves a to parse a .
+ ///
+ /// A stream pointing to the .
+ public static Decoder GetDecoder(StreamReader stream)
+ {
+ if (stream == null)
+ throw new ArgumentNullException(nameof(stream));
+
+ string line;
+ do
+ { line = stream.ReadLine()?.Trim(); }
+ while (line != null && line.Length == 0);
+
+ if (line == null || !decoders.ContainsKey(line))
+ throw new IOException(@"Unknown file format");
+ return (Decoder)Activator.CreateInstance(decoders[line], line);
+ }
+
+ ///
+ /// Adds the to the list of and decoder.
+ ///
+ /// Type to decode a with.
+ /// A string representation of the version.
+ protected static void AddDecoder(string version) where T : Decoder
+ {
+ decoders[version] = typeof(T);
+ }
+
+ ///
+ /// Retrieves a to parse a
+ ///
+ public abstract Decoder GetStoryboardDecoder();
+
+ public virtual Beatmap DecodeBeatmap(StreamReader stream)
+ {
+ var beatmap = new Beatmap
+ {
+ BeatmapInfo = new BeatmapInfo
+ {
+ Metadata = new BeatmapMetadata(),
+ BaseDifficulty = new BeatmapDifficulty(),
+ },
+ };
+
+ ParseBeatmap(stream, beatmap);
+ return beatmap;
+ }
+
+ protected abstract void ParseBeatmap(StreamReader stream, Beatmap beatmap);
+
+ public virtual Storyboard DecodeStoryboard(StreamReader stream)
+ {
+ var storyboard = new Storyboard();
+ ParseStoryboard(stream, storyboard);
+ return storyboard;
+ }
+
+ protected abstract void ParseStoryboard(StreamReader stream, Storyboard storyboard);
+ }
+}
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
new file mode 100644
index 0000000000..b7004dd3eb
--- /dev/null
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -0,0 +1,421 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Globalization;
+using System.IO;
+using OpenTK.Graphics;
+using osu.Game.Beatmaps.Timing;
+using osu.Game.Rulesets.Objects.Legacy;
+using osu.Game.Beatmaps.ControlPoints;
+using System.Collections.Generic;
+
+namespace osu.Game.Beatmaps.Formats
+{
+ public class LegacyBeatmapDecoder : LegacyDecoder
+ {
+ private Beatmap beatmap;
+
+ private bool hasCustomColours;
+ private ConvertHitObjectParser parser;
+
+ private LegacySampleBank defaultSampleBank;
+ private int defaultSampleVolume = 100;
+
+ public LegacyBeatmapDecoder()
+ {
+ }
+
+ public LegacyBeatmapDecoder(string header)
+ {
+ BeatmapVersion = int.Parse(header.Substring(17));
+ }
+
+ protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap)
+ {
+ if (stream == null)
+ throw new ArgumentNullException(nameof(stream));
+ if (beatmap == null)
+ throw new ArgumentNullException(nameof(beatmap));
+
+ this.beatmap = beatmap;
+ this.beatmap.BeatmapInfo.BeatmapVersion = BeatmapVersion;
+
+ ParseContent(stream);
+
+ foreach (var hitObject in this.beatmap.HitObjects)
+ hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty);
+ }
+
+ protected override bool ShouldSkipLine(string line)
+ {
+ if (base.ShouldSkipLine(line) || line.StartsWith(" ") || line.StartsWith("_"))
+ return true;
+ return false;
+ }
+
+ protected override void ProcessSection(Section section, string line)
+ {
+ switch (section)
+ {
+ case Section.General:
+ handleGeneral(line);
+ break;
+ case Section.Editor:
+ handleEditor(line);
+ break;
+ case Section.Metadata:
+ handleMetadata(line);
+ break;
+ case Section.Difficulty:
+ handleDifficulty(line);
+ break;
+ case Section.Events:
+ handleEvents(line);
+ break;
+ case Section.TimingPoints:
+ handleTimingPoints(line);
+ break;
+ case Section.Colours:
+ handleColours(line);
+ break;
+ case Section.HitObjects:
+ handleHitObjects(line);
+ break;
+ case Section.Variables:
+ handleVariables(line);
+ break;
+ }
+ }
+
+ private void handleGeneral(string line)
+ {
+ var pair = splitKeyVal(line, ':');
+
+ var metadata = beatmap.BeatmapInfo.Metadata;
+ switch (pair.Key)
+ {
+ case @"AudioFilename":
+ metadata.AudioFile = pair.Value;
+ break;
+ case @"AudioLeadIn":
+ beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value);
+ break;
+ case @"PreviewTime":
+ metadata.PreviewTime = int.Parse(pair.Value);
+ break;
+ case @"Countdown":
+ beatmap.BeatmapInfo.Countdown = int.Parse(pair.Value) == 1;
+ break;
+ case @"SampleSet":
+ defaultSampleBank = (LegacySampleBank)Enum.Parse(typeof(LegacySampleBank), pair.Value);
+ break;
+ case @"SampleVolume":
+ defaultSampleVolume = int.Parse(pair.Value);
+ break;
+ case @"StackLeniency":
+ beatmap.BeatmapInfo.StackLeniency = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo);
+ break;
+ case @"Mode":
+ beatmap.BeatmapInfo.RulesetID = int.Parse(pair.Value);
+
+ switch (beatmap.BeatmapInfo.RulesetID)
+ {
+ case 0:
+ parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser();
+ break;
+ case 1:
+ parser = new Rulesets.Objects.Legacy.Taiko.ConvertHitObjectParser();
+ break;
+ case 2:
+ parser = new Rulesets.Objects.Legacy.Catch.ConvertHitObjectParser();
+ break;
+ case 3:
+ parser = new Rulesets.Objects.Legacy.Mania.ConvertHitObjectParser();
+ break;
+ }
+ break;
+ case @"LetterboxInBreaks":
+ beatmap.BeatmapInfo.LetterboxInBreaks = int.Parse(pair.Value) == 1;
+ break;
+ case @"SpecialStyle":
+ beatmap.BeatmapInfo.SpecialStyle = int.Parse(pair.Value) == 1;
+ break;
+ case @"WidescreenStoryboard":
+ beatmap.BeatmapInfo.WidescreenStoryboard = int.Parse(pair.Value) == 1;
+ break;
+ }
+ }
+
+ private void handleEditor(string line)
+ {
+ var pair = splitKeyVal(line, ':');
+
+ switch (pair.Key)
+ {
+ case @"Bookmarks":
+ beatmap.BeatmapInfo.StoredBookmarks = pair.Value;
+ break;
+ case @"DistanceSpacing":
+ beatmap.BeatmapInfo.DistanceSpacing = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo);
+ break;
+ case @"BeatDivisor":
+ beatmap.BeatmapInfo.BeatDivisor = int.Parse(pair.Value);
+ break;
+ case @"GridSize":
+ beatmap.BeatmapInfo.GridSize = int.Parse(pair.Value);
+ break;
+ case @"TimelineZoom":
+ beatmap.BeatmapInfo.TimelineZoom = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo);
+ break;
+ }
+ }
+
+ private void handleMetadata(string line)
+ {
+ var pair = splitKeyVal(line, ':');
+
+ var metadata = beatmap.BeatmapInfo.Metadata;
+ switch (pair.Key)
+ {
+ case @"Title":
+ metadata.Title = pair.Value;
+ break;
+ case @"TitleUnicode":
+ metadata.TitleUnicode = pair.Value;
+ break;
+ case @"Artist":
+ metadata.Artist = pair.Value;
+ break;
+ case @"ArtistUnicode":
+ metadata.ArtistUnicode = pair.Value;
+ break;
+ case @"Creator":
+ metadata.AuthorString = pair.Value;
+ break;
+ case @"Version":
+ beatmap.BeatmapInfo.Version = pair.Value;
+ break;
+ case @"Source":
+ beatmap.BeatmapInfo.Metadata.Source = pair.Value;
+ break;
+ case @"Tags":
+ beatmap.BeatmapInfo.Metadata.Tags = pair.Value;
+ break;
+ case @"BeatmapID":
+ beatmap.BeatmapInfo.OnlineBeatmapID = int.Parse(pair.Value);
+ break;
+ case @"BeatmapSetID":
+ beatmap.BeatmapInfo.OnlineBeatmapSetID = int.Parse(pair.Value);
+ metadata.OnlineBeatmapSetID = int.Parse(pair.Value);
+ break;
+ }
+ }
+
+ private void handleDifficulty(string line)
+ {
+ var pair = splitKeyVal(line, ':');
+
+ var difficulty = beatmap.BeatmapInfo.BaseDifficulty;
+ switch (pair.Key)
+ {
+ case @"HPDrainRate":
+ difficulty.DrainRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo);
+ break;
+ case @"CircleSize":
+ difficulty.CircleSize = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo);
+ break;
+ case @"OverallDifficulty":
+ difficulty.OverallDifficulty = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo);
+ break;
+ case @"ApproachRate":
+ difficulty.ApproachRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo);
+ break;
+ case @"SliderMultiplier":
+ difficulty.SliderMultiplier = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo);
+ break;
+ case @"SliderTickRate":
+ difficulty.SliderTickRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo);
+ break;
+ }
+ }
+
+ private void handleEvents(string line)
+ {
+ DecodeVariables(ref line);
+
+ string[] split = line.Split(',');
+
+ EventType type;
+ if (!Enum.TryParse(split[0], out type))
+ throw new InvalidDataException($@"Unknown event type {split[0]}");
+
+ switch (type)
+ {
+ case EventType.Background:
+ string filename = split[2].Trim('"');
+ beatmap.BeatmapInfo.Metadata.BackgroundFile = filename;
+ break;
+ case EventType.Break:
+ var breakEvent = new BreakPeriod
+ {
+ StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo),
+ EndTime = double.Parse(split[2], NumberFormatInfo.InvariantInfo)
+ };
+
+ if (!breakEvent.HasEffect)
+ return;
+
+ beatmap.Breaks.Add(breakEvent);
+ break;
+ }
+ }
+
+ private void handleTimingPoints(string line)
+ {
+ string[] split = line.Split(',');
+
+ double time = double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo);
+ double beatLength = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo);
+ double speedMultiplier = beatLength < 0 ? 100.0 / -beatLength : 1;
+
+ TimeSignatures timeSignature = TimeSignatures.SimpleQuadruple;
+ if (split.Length >= 3)
+ timeSignature = split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)int.Parse(split[2]);
+
+ LegacySampleBank sampleSet = defaultSampleBank;
+ if (split.Length >= 4)
+ sampleSet = (LegacySampleBank)int.Parse(split[3]);
+
+ //SampleBank sampleBank = SampleBank.Default;
+ //if (split.Length >= 5)
+ // sampleBank = (SampleBank)int.Parse(split[4]);
+
+ int sampleVolume = defaultSampleVolume;
+ if (split.Length >= 6)
+ sampleVolume = int.Parse(split[5]);
+
+ bool timingChange = true;
+ if (split.Length >= 7)
+ timingChange = split[6][0] == '1';
+
+ bool kiaiMode = false;
+ bool omitFirstBarSignature = false;
+ if (split.Length >= 8)
+ {
+ int effectFlags = int.Parse(split[7]);
+ kiaiMode = (effectFlags & 1) > 0;
+ omitFirstBarSignature = (effectFlags & 8) > 0;
+ }
+
+ string stringSampleSet = sampleSet.ToString().ToLower();
+ if (stringSampleSet == @"none")
+ stringSampleSet = @"normal";
+
+ DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(time);
+ SoundControlPoint soundPoint = beatmap.ControlPointInfo.SoundPointAt(time);
+ EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(time);
+
+ if (timingChange)
+ {
+ beatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint
+ {
+ Time = time,
+ BeatLength = beatLength,
+ TimeSignature = timeSignature
+ });
+ }
+
+ if (speedMultiplier != difficultyPoint.SpeedMultiplier)
+ {
+ beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == time);
+ beatmap.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint
+ {
+ Time = time,
+ SpeedMultiplier = speedMultiplier
+ });
+ }
+
+ if (stringSampleSet != soundPoint.SampleBank || sampleVolume != soundPoint.SampleVolume)
+ {
+ beatmap.ControlPointInfo.SoundPoints.Add(new SoundControlPoint
+ {
+ Time = time,
+ SampleBank = stringSampleSet,
+ SampleVolume = sampleVolume
+ });
+ }
+
+ if (kiaiMode != effectPoint.KiaiMode || omitFirstBarSignature != effectPoint.OmitFirstBarLine)
+ {
+ beatmap.ControlPointInfo.EffectPoints.Add(new EffectControlPoint
+ {
+ Time = time,
+ KiaiMode = kiaiMode,
+ OmitFirstBarLine = omitFirstBarSignature
+ });
+ }
+ }
+
+ private void handleColours(string line)
+ {
+ var pair = splitKeyVal(line, ':');
+
+ string[] split = pair.Value.Split(',');
+
+ if (split.Length != 3)
+ throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {pair.Value}");
+
+ byte r, g, b;
+ if (!byte.TryParse(split[0], out r) || !byte.TryParse(split[1], out g) || !byte.TryParse(split[2], out b))
+ throw new InvalidOperationException(@"Color must be specified with 8-bit integer components");
+
+ if (!hasCustomColours)
+ {
+ beatmap.ComboColors.Clear();
+ hasCustomColours = true;
+ }
+
+ // Note: the combo index specified in the beatmap is discarded
+ if (pair.Key.StartsWith(@"Combo"))
+ {
+ beatmap.ComboColors.Add(new Color4
+ {
+ R = r / 255f,
+ G = g / 255f,
+ B = b / 255f,
+ A = 1f,
+ });
+ }
+ }
+
+ private void handleHitObjects(string line)
+ {
+ // If the ruleset wasn't specified, assume the osu!standard ruleset.
+ if (parser == null)
+ parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser();
+
+ var obj = parser.Parse(line);
+
+ if (obj != null)
+ beatmap.HitObjects.Add(obj);
+ }
+
+ private void handleVariables(string line)
+ {
+ var pair = splitKeyVal(line, '=');
+ Variables[pair.Key] = pair.Value;
+ }
+
+ private KeyValuePair splitKeyVal(string line, char separator)
+ {
+ var split = line.Trim().Split(new[] { separator }, 2);
+
+ return new KeyValuePair
+ (
+ split[0].Trim(),
+ split.Length > 1 ? split[1].Trim() : string.Empty
+ );
+ }
+ }
+}
diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
new file mode 100644
index 0000000000..96747a870d
--- /dev/null
+++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
@@ -0,0 +1,163 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using osu.Game.Beatmaps.Legacy;
+using osu.Game.Storyboards;
+
+namespace osu.Game.Beatmaps.Formats
+{
+ public abstract class LegacyDecoder : Decoder
+ {
+ public static void Register()
+ {
+ AddDecoder(@"osu file format v14");
+ AddDecoder(@"osu file format v13");
+ AddDecoder(@"osu file format v12");
+ AddDecoder(@"osu file format v11");
+ AddDecoder(@"osu file format v10");
+ AddDecoder(@"osu file format v9");
+ AddDecoder(@"osu file format v8");
+ AddDecoder(@"osu file format v7");
+ AddDecoder(@"osu file format v6");
+ AddDecoder(@"osu file format v5");
+ AddDecoder(@"osu file format v4");
+ AddDecoder(@"osu file format v3");
+ // TODO: differences between versions
+ }
+
+ protected int BeatmapVersion;
+ protected readonly Dictionary Variables = new Dictionary();
+
+ public override Decoder GetStoryboardDecoder() => new LegacyStoryboardDecoder(BeatmapVersion);
+
+ public override Beatmap DecodeBeatmap(StreamReader stream) => new LegacyBeatmap(base.DecodeBeatmap(stream));
+
+ protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected void ParseContent(StreamReader stream)
+ {
+ Section section = Section.None;
+
+ string line;
+ while ((line = stream.ReadLine()) != null)
+ {
+ if (ShouldSkipLine(line))
+ continue;
+
+ // It's already set in ParseBeatmap... why do it again?
+ //if (line.StartsWith(@"osu file format v"))
+ //{
+ // Beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17));
+ // continue;
+ //}
+
+ if (line.StartsWith(@"[") && line.EndsWith(@"]"))
+ {
+ if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section))
+ throw new InvalidDataException($@"Unknown osu section {line}");
+ continue;
+ }
+
+ ProcessSection(section, line);
+ }
+ }
+
+ protected virtual bool ShouldSkipLine(string line)
+ {
+ if (string.IsNullOrWhiteSpace(line) || line.StartsWith("//"))
+ return true;
+ return false;
+ }
+
+ protected abstract void ProcessSection(Section section, string line);
+
+ ///
+ /// Decodes any beatmap variables present in a line into their real values.
+ ///
+ /// The line which may contains variables.
+ protected void DecodeVariables(ref string line)
+ {
+ while (line.IndexOf('$') >= 0)
+ {
+ string origLine = line;
+ string[] split = line.Split(',');
+ for (int i = 0; i < split.Length; i++)
+ {
+ var item = split[i];
+ if (item.StartsWith("$") && Variables.ContainsKey(item))
+ split[i] = Variables[item];
+ }
+
+ line = string.Join(",", split);
+ if (line == origLine)
+ break;
+ }
+ }
+
+ protected enum Section
+ {
+ None,
+ General,
+ Editor,
+ Metadata,
+ Difficulty,
+ Events,
+ TimingPoints,
+ Colours,
+ HitObjects,
+ Variables,
+ }
+
+ internal enum LegacySampleBank
+ {
+ None = 0,
+ Normal = 1,
+ Soft = 2,
+ Drum = 3
+ }
+
+ internal enum EventType
+ {
+ Background = 0,
+ Video = 1,
+ Break = 2,
+ Colour = 3,
+ Sprite = 4,
+ Sample = 5,
+ Animation = 6
+ }
+
+ internal enum LegacyOrigins
+ {
+ TopLeft,
+ Centre,
+ CentreLeft,
+ TopRight,
+ BottomCentre,
+ TopCentre,
+ Custom,
+ CentreRight,
+ BottomLeft,
+ BottomRight
+ };
+
+ internal enum StoryLayer
+ {
+ Background = 0,
+ Fail = 1,
+ Pass = 2,
+ Foreground = 3
+ }
+ }
+}
diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
new file mode 100644
index 0000000000..8da6a0cefb
--- /dev/null
+++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
@@ -0,0 +1,271 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Globalization;
+using System.IO;
+using OpenTK;
+using OpenTK.Graphics;
+using osu.Framework.Graphics;
+using osu.Framework.IO.File;
+using osu.Game.Storyboards;
+
+namespace osu.Game.Beatmaps.Formats
+{
+ public class LegacyStoryboardDecoder : LegacyDecoder
+ {
+ private Storyboard storyboard;
+
+ private StoryboardSprite storyboardSprite;
+ private CommandTimelineGroup timelineGroup;
+
+ public LegacyStoryboardDecoder()
+ {
+ }
+
+ public LegacyStoryboardDecoder(int beatmapVersion)
+ {
+ BeatmapVersion = beatmapVersion;
+ }
+
+ protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard)
+ {
+ if (stream == null)
+ throw new ArgumentNullException(nameof(stream));
+ if (storyboard == null)
+ throw new ArgumentNullException(nameof(storyboard));
+
+ this.storyboard = storyboard;
+
+ ParseContent(stream);
+ }
+
+ protected override void ProcessSection(Section section, string line)
+ {
+ switch (section)
+ {
+ case Section.Events:
+ handleEvents(line);
+ break;
+ }
+ }
+
+ private void handleEvents(string line)
+ {
+ var depth = 0;
+ while (line.StartsWith(" ") || line.StartsWith("_"))
+ {
+ ++depth;
+ line = line.Substring(1);
+ }
+
+ DecodeVariables(ref line);
+
+ string[] split = line.Split(',');
+
+ if (depth == 0)
+ {
+ storyboardSprite = null;
+
+ EventType type;
+ if (!Enum.TryParse(split[0], out type))
+ throw new InvalidDataException($@"Unknown event type {split[0]}");
+
+ switch (type)
+ {
+ case EventType.Sprite:
+ {
+ var layer = parseLayer(split[1]);
+ var origin = parseOrigin(split[2]);
+ var path = cleanFilename(split[3]);
+ var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo);
+ var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo);
+ storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y));
+ storyboard.GetLayer(layer).Add(storyboardSprite);
+ }
+ break;
+ case EventType.Animation:
+ {
+ var layer = parseLayer(split[1]);
+ var origin = parseOrigin(split[2]);
+ var path = cleanFilename(split[3]);
+ var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo);
+ var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo);
+ var frameCount = int.Parse(split[6]);
+ var frameDelay = double.Parse(split[7], NumberFormatInfo.InvariantInfo);
+ var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever;
+ storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType);
+ storyboard.GetLayer(layer).Add(storyboardSprite);
+ }
+ break;
+ case EventType.Sample:
+ {
+ var time = double.Parse(split[1], CultureInfo.InvariantCulture);
+ var layer = parseLayer(split[2]);
+ var path = cleanFilename(split[3]);
+ var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100;
+ storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume));
+ }
+ break;
+ }
+ }
+ else
+ {
+ if (depth < 2)
+ timelineGroup = storyboardSprite?.TimelineGroup;
+
+ var commandType = split[0];
+ switch (commandType)
+ {
+ case "T":
+ {
+ var triggerName = split[1];
+ var startTime = split.Length > 2 ? double.Parse(split[2], CultureInfo.InvariantCulture) : double.MinValue;
+ var endTime = split.Length > 3 ? double.Parse(split[3], CultureInfo.InvariantCulture) : double.MaxValue;
+ var groupNumber = split.Length > 4 ? int.Parse(split[4]) : 0;
+ timelineGroup = storyboardSprite?.AddTrigger(triggerName, startTime, endTime, groupNumber);
+ }
+ break;
+ case "L":
+ {
+ var startTime = double.Parse(split[1], CultureInfo.InvariantCulture);
+ var loopCount = int.Parse(split[2]);
+ timelineGroup = storyboardSprite?.AddLoop(startTime, loopCount);
+ }
+ break;
+ default:
+ {
+ if (string.IsNullOrEmpty(split[3]))
+ split[3] = split[2];
+
+ var easing = (Easing)int.Parse(split[1]);
+ var startTime = double.Parse(split[2], CultureInfo.InvariantCulture);
+ var endTime = double.Parse(split[3], CultureInfo.InvariantCulture);
+
+ switch (commandType)
+ {
+ case "F":
+ {
+ var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
+ var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
+ timelineGroup?.Alpha.Add(easing, startTime, endTime, startValue, endValue);
+ }
+ break;
+ case "S":
+ {
+ var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
+ var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
+ timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startValue), new Vector2(endValue));
+ }
+ break;
+ case "V":
+ {
+ var startX = float.Parse(split[4], CultureInfo.InvariantCulture);
+ var startY = float.Parse(split[5], CultureInfo.InvariantCulture);
+ var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX;
+ var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY;
+ timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY));
+ }
+ break;
+ case "R":
+ {
+ var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
+ var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
+ timelineGroup?.Rotation.Add(easing, startTime, endTime, MathHelper.RadiansToDegrees(startValue), MathHelper.RadiansToDegrees(endValue));
+ }
+ break;
+ case "M":
+ {
+ var startX = float.Parse(split[4], CultureInfo.InvariantCulture);
+ var startY = float.Parse(split[5], CultureInfo.InvariantCulture);
+ var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX;
+ var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY;
+ timelineGroup?.X.Add(easing, startTime, endTime, startX, endX);
+ timelineGroup?.Y.Add(easing, startTime, endTime, startY, endY);
+ }
+ break;
+ case "MX":
+ {
+ var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
+ var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
+ timelineGroup?.X.Add(easing, startTime, endTime, startValue, endValue);
+ }
+ break;
+ case "MY":
+ {
+ var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
+ var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
+ timelineGroup?.Y.Add(easing, startTime, endTime, startValue, endValue);
+ }
+ break;
+ case "C":
+ {
+ var startRed = float.Parse(split[4], CultureInfo.InvariantCulture);
+ var startGreen = float.Parse(split[5], CultureInfo.InvariantCulture);
+ var startBlue = float.Parse(split[6], CultureInfo.InvariantCulture);
+ var endRed = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startRed;
+ var endGreen = split.Length > 8 ? float.Parse(split[8], CultureInfo.InvariantCulture) : startGreen;
+ var endBlue = split.Length > 9 ? float.Parse(split[9], CultureInfo.InvariantCulture) : startBlue;
+ timelineGroup?.Colour.Add(easing, startTime, endTime,
+ new Color4(startRed / 255f, startGreen / 255f, startBlue / 255f, 1),
+ new Color4(endRed / 255f, endGreen / 255f, endBlue / 255f, 1));
+ }
+ break;
+ case "P":
+ {
+ var type = split[4];
+ switch (type)
+ {
+ case "A":
+ timelineGroup?.BlendingMode.Add(easing, startTime, endTime, BlendingMode.Additive, startTime == endTime ? BlendingMode.Additive : BlendingMode.Inherit);
+ break;
+ case "H":
+ timelineGroup?.FlipH.Add(easing, startTime, endTime, true, startTime == endTime);
+ break;
+ case "V":
+ timelineGroup?.FlipV.Add(easing, startTime, endTime, true, startTime == endTime);
+ break;
+ }
+ }
+ break;
+ default:
+ throw new InvalidDataException($@"Unknown command type: {commandType}");
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ private string parseLayer(string value) => Enum.Parse(typeof(StoryLayer), value).ToString();
+
+ private Anchor parseOrigin(string value)
+ {
+ var origin = (LegacyOrigins)Enum.Parse(typeof(LegacyOrigins), value);
+ switch (origin)
+ {
+ case LegacyOrigins.TopLeft:
+ return Anchor.TopLeft;
+ case LegacyOrigins.TopCentre:
+ return Anchor.TopCentre;
+ case LegacyOrigins.TopRight:
+ return Anchor.TopRight;
+ case LegacyOrigins.CentreLeft:
+ return Anchor.CentreLeft;
+ case LegacyOrigins.Centre:
+ return Anchor.Centre;
+ case LegacyOrigins.CentreRight:
+ return Anchor.CentreRight;
+ case LegacyOrigins.BottomLeft:
+ return Anchor.BottomLeft;
+ case LegacyOrigins.BottomCentre:
+ return Anchor.BottomCentre;
+ case LegacyOrigins.BottomRight:
+ return Anchor.BottomRight;
+ }
+ throw new InvalidDataException($@"Unknown origin: {value}");
+ }
+
+ private string cleanFilename(string path) => FileSafety.PathStandardise(path.Trim('\"'));
+ }
+}
diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs
deleted file mode 100644
index 11631e9447..0000000000
--- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs
+++ /dev/null
@@ -1,781 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using OpenTK.Graphics;
-using osu.Game.Beatmaps.Timing;
-using osu.Game.Beatmaps.Legacy;
-using osu.Game.Rulesets.Objects.Legacy;
-using osu.Game.Beatmaps.ControlPoints;
-using osu.Game.Storyboards;
-using OpenTK;
-using osu.Framework.Graphics;
-using osu.Framework.IO.File;
-
-namespace osu.Game.Beatmaps.Formats
-{
- public class OsuLegacyDecoder : BeatmapDecoder
- {
- public static void Register()
- {
- AddDecoder(@"osu file format v14");
- AddDecoder(@"osu file format v13");
- AddDecoder(@"osu file format v12");
- AddDecoder(@"osu file format v11");
- AddDecoder(@"osu file format v10");
- AddDecoder(@"osu file format v9");
- AddDecoder(@"osu file format v8");
- AddDecoder(@"osu file format v7");
- AddDecoder(@"osu file format v6");
- AddDecoder(@"osu file format v5");
- AddDecoder(@"osu file format v4");
- AddDecoder(@"osu file format v3");
- // TODO: differences between versions
- }
-
- private ConvertHitObjectParser parser;
-
- private readonly Dictionary variables = new Dictionary();
-
- private LegacySampleBank defaultSampleBank;
- private int defaultSampleVolume = 100;
-
- private readonly int beatmapVersion;
-
- public OsuLegacyDecoder()
- {
- }
-
- public OsuLegacyDecoder(string header)
- {
- beatmapVersion = int.Parse(header.Substring(17));
- }
-
- private enum Section
- {
- None,
- General,
- Editor,
- Metadata,
- Difficulty,
- Events,
- TimingPoints,
- Colours,
- HitObjects,
- Variables,
- }
-
- private void handleGeneral(Beatmap beatmap, string line)
- {
- if (beatmap == null)
- throw new ArgumentNullException(nameof(beatmap));
- if (line == null)
- throw new ArgumentNullException(nameof(line));
-
- var pair = splitKeyVal(line, ':');
-
- var metadata = beatmap.BeatmapInfo.Metadata;
- switch (pair.Key)
- {
- case @"AudioFilename":
- metadata.AudioFile = pair.Value;
- break;
- case @"AudioLeadIn":
- beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value);
- break;
- case @"PreviewTime":
- metadata.PreviewTime = int.Parse(pair.Value);
- break;
- case @"Countdown":
- beatmap.BeatmapInfo.Countdown = int.Parse(pair.Value) == 1;
- break;
- case @"SampleSet":
- defaultSampleBank = (LegacySampleBank)Enum.Parse(typeof(LegacySampleBank), pair.Value);
- break;
- case @"SampleVolume":
- defaultSampleVolume = int.Parse(pair.Value);
- break;
- case @"StackLeniency":
- beatmap.BeatmapInfo.StackLeniency = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo);
- break;
- case @"Mode":
- beatmap.BeatmapInfo.RulesetID = int.Parse(pair.Value);
-
- switch (beatmap.BeatmapInfo.RulesetID)
- {
- case 0:
- parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser();
- break;
- case 1:
- parser = new Rulesets.Objects.Legacy.Taiko.ConvertHitObjectParser();
- break;
- case 2:
- parser = new Rulesets.Objects.Legacy.Catch.ConvertHitObjectParser();
- break;
- case 3:
- parser = new Rulesets.Objects.Legacy.Mania.ConvertHitObjectParser();
- break;
- }
- break;
- case @"LetterboxInBreaks":
- beatmap.BeatmapInfo.LetterboxInBreaks = int.Parse(pair.Value) == 1;
- break;
- case @"SpecialStyle":
- beatmap.BeatmapInfo.SpecialStyle = int.Parse(pair.Value) == 1;
- break;
- case @"WidescreenStoryboard":
- beatmap.BeatmapInfo.WidescreenStoryboard = int.Parse(pair.Value) == 1;
- break;
- }
- }
-
- private void handleEditor(Beatmap beatmap, string line)
- {
- if (beatmap == null)
- throw new ArgumentNullException(nameof(beatmap));
- if (line == null)
- throw new ArgumentNullException(nameof(line));
-
- var pair = splitKeyVal(line, ':');
-
- switch (pair.Key)
- {
- case @"Bookmarks":
- beatmap.BeatmapInfo.StoredBookmarks = pair.Value;
- break;
- case @"DistanceSpacing":
- beatmap.BeatmapInfo.DistanceSpacing = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo);
- break;
- case @"BeatDivisor":
- beatmap.BeatmapInfo.BeatDivisor = int.Parse(pair.Value);
- break;
- case @"GridSize":
- beatmap.BeatmapInfo.GridSize = int.Parse(pair.Value);
- break;
- case @"TimelineZoom":
- beatmap.BeatmapInfo.TimelineZoom = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo);
- break;
- }
- }
-
- private void handleMetadata(Beatmap beatmap, string line)
- {
- if (beatmap == null)
- throw new ArgumentNullException(nameof(beatmap));
- if (line == null)
- throw new ArgumentNullException(nameof(line));
-
- var pair = splitKeyVal(line, ':');
-
- var metadata = beatmap.BeatmapInfo.Metadata;
- switch (pair.Key)
- {
- case @"Title":
- metadata.Title = pair.Value;
- break;
- case @"TitleUnicode":
- metadata.TitleUnicode = pair.Value;
- break;
- case @"Artist":
- metadata.Artist = pair.Value;
- break;
- case @"ArtistUnicode":
- metadata.ArtistUnicode = pair.Value;
- break;
- case @"Creator":
- metadata.AuthorString = pair.Value;
- break;
- case @"Version":
- beatmap.BeatmapInfo.Version = pair.Value;
- break;
- case @"Source":
- beatmap.BeatmapInfo.Metadata.Source = pair.Value;
- break;
- case @"Tags":
- beatmap.BeatmapInfo.Metadata.Tags = pair.Value;
- break;
- case @"BeatmapID":
- beatmap.BeatmapInfo.OnlineBeatmapID = int.Parse(pair.Value);
- break;
- case @"BeatmapSetID":
- beatmap.BeatmapInfo.OnlineBeatmapSetID = int.Parse(pair.Value);
- metadata.OnlineBeatmapSetID = int.Parse(pair.Value);
- break;
- }
- }
-
- private void handleDifficulty(Beatmap beatmap, string line)
- {
- if (beatmap == null)
- throw new ArgumentNullException(nameof(beatmap));
- if (line == null)
- throw new ArgumentNullException(nameof(line));
-
- var pair = splitKeyVal(line, ':');
-
- var difficulty = beatmap.BeatmapInfo.BaseDifficulty;
- switch (pair.Key)
- {
- case @"HPDrainRate":
- difficulty.DrainRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo);
- break;
- case @"CircleSize":
- difficulty.CircleSize = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo);
- break;
- case @"OverallDifficulty":
- difficulty.OverallDifficulty = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo);
- break;
- case @"ApproachRate":
- difficulty.ApproachRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo);
- break;
- case @"SliderMultiplier":
- difficulty.SliderMultiplier = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo);
- break;
- case @"SliderTickRate":
- difficulty.SliderTickRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo);
- break;
- }
- }
-
- ///
- /// Decodes any beatmap variables present in a line into their real values.
- ///
- /// The line which may contains variables.
- private void decodeVariables(ref string line)
- {
- if (line == null)
- throw new ArgumentNullException(nameof(line));
-
- while (line.IndexOf('$') >= 0)
- {
- string origLine = line;
- string[] split = line.Split(',');
- for (int i = 0; i < split.Length; i++)
- {
- var item = split[i];
- if (item.StartsWith("$") && variables.ContainsKey(item))
- split[i] = variables[item];
- }
-
- line = string.Join(",", split);
- if (line == origLine) break;
- }
- }
-
- private void handleEvents(Beatmap beatmap, string line, ref StoryboardSprite storyboardSprite, ref CommandTimelineGroup timelineGroup)
- {
- if (line == null)
- throw new ArgumentNullException(nameof(line));
- if (beatmap == null)
- throw new ArgumentNullException(nameof(beatmap));
-
- var depth = 0;
- while (line.StartsWith(" ") || line.StartsWith("_"))
- {
- ++depth;
- line = line.Substring(1);
- }
-
- decodeVariables(ref line);
-
- string[] split = line.Split(',');
-
- if (depth == 0)
- {
- storyboardSprite = null;
-
- EventType type;
- if (!Enum.TryParse(split[0], out type))
- throw new InvalidDataException($@"Unknown event type {split[0]}");
-
- switch (type)
- {
- case EventType.Video:
- case EventType.Background:
- string filename = split[2].Trim('"');
-
- if (type == EventType.Background)
- beatmap.BeatmapInfo.Metadata.BackgroundFile = filename;
-
- break;
- case EventType.Break:
- var breakEvent = new BreakPeriod
- {
- StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo),
- EndTime = double.Parse(split[2], NumberFormatInfo.InvariantInfo)
- };
-
- if (!breakEvent.HasEffect)
- return;
-
- beatmap.Breaks.Add(breakEvent);
- break;
- case EventType.Sprite:
- {
- var layer = parseLayer(split[1]);
- var origin = parseOrigin(split[2]);
- var path = cleanFilename(split[3]);
- var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo);
- var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo);
- storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y));
- beatmap.Storyboard.GetLayer(layer).Add(storyboardSprite);
- }
- break;
- case EventType.Animation:
- {
- var layer = parseLayer(split[1]);
- var origin = parseOrigin(split[2]);
- var path = cleanFilename(split[3]);
- var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo);
- var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo);
- var frameCount = int.Parse(split[6]);
- var frameDelay = double.Parse(split[7], NumberFormatInfo.InvariantInfo);
- var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever;
- storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType);
- beatmap.Storyboard.GetLayer(layer).Add(storyboardSprite);
- }
- break;
- case EventType.Sample:
- {
- var time = double.Parse(split[1], CultureInfo.InvariantCulture);
- var layer = parseLayer(split[2]);
- var path = cleanFilename(split[3]);
- var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100;
- beatmap.Storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume));
- }
- break;
- }
- }
- else
- {
- if (depth < 2)
- timelineGroup = storyboardSprite?.TimelineGroup;
-
- var commandType = split[0];
- switch (commandType)
- {
- case "T":
- {
- var triggerName = split[1];
- var startTime = split.Length > 2 ? double.Parse(split[2], CultureInfo.InvariantCulture) : double.MinValue;
- var endTime = split.Length > 3 ? double.Parse(split[3], CultureInfo.InvariantCulture) : double.MaxValue;
- var groupNumber = split.Length > 4 ? int.Parse(split[4]) : 0;
- timelineGroup = storyboardSprite?.AddTrigger(triggerName, startTime, endTime, groupNumber);
- }
- break;
- case "L":
- {
- var startTime = double.Parse(split[1], CultureInfo.InvariantCulture);
- var loopCount = int.Parse(split[2]);
- timelineGroup = storyboardSprite?.AddLoop(startTime, loopCount);
- }
- break;
- default:
- {
- if (string.IsNullOrEmpty(split[3]))
- split[3] = split[2];
-
- var easing = (Easing)int.Parse(split[1]);
- var startTime = double.Parse(split[2], CultureInfo.InvariantCulture);
- var endTime = double.Parse(split[3], CultureInfo.InvariantCulture);
-
- switch (commandType)
- {
- case "F":
- {
- var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
- var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
- timelineGroup?.Alpha.Add(easing, startTime, endTime, startValue, endValue);
- }
- break;
- case "S":
- {
- var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
- var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
- timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startValue), new Vector2(endValue));
- }
- break;
- case "V":
- {
- var startX = float.Parse(split[4], CultureInfo.InvariantCulture);
- var startY = float.Parse(split[5], CultureInfo.InvariantCulture);
- var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX;
- var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY;
- timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY));
- }
- break;
- case "R":
- {
- var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
- var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
- timelineGroup?.Rotation.Add(easing, startTime, endTime, MathHelper.RadiansToDegrees(startValue), MathHelper.RadiansToDegrees(endValue));
- }
- break;
- case "M":
- {
- var startX = float.Parse(split[4], CultureInfo.InvariantCulture);
- var startY = float.Parse(split[5], CultureInfo.InvariantCulture);
- var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX;
- var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY;
- timelineGroup?.X.Add(easing, startTime, endTime, startX, endX);
- timelineGroup?.Y.Add(easing, startTime, endTime, startY, endY);
- }
- break;
- case "MX":
- {
- var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
- var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
- timelineGroup?.X.Add(easing, startTime, endTime, startValue, endValue);
- }
- break;
- case "MY":
- {
- var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
- var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
- timelineGroup?.Y.Add(easing, startTime, endTime, startValue, endValue);
- }
- break;
- case "C":
- {
- var startRed = float.Parse(split[4], CultureInfo.InvariantCulture);
- var startGreen = float.Parse(split[5], CultureInfo.InvariantCulture);
- var startBlue = float.Parse(split[6], CultureInfo.InvariantCulture);
- var endRed = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startRed;
- var endGreen = split.Length > 8 ? float.Parse(split[8], CultureInfo.InvariantCulture) : startGreen;
- var endBlue = split.Length > 9 ? float.Parse(split[9], CultureInfo.InvariantCulture) : startBlue;
- timelineGroup?.Colour.Add(easing, startTime, endTime,
- new Color4(startRed / 255f, startGreen / 255f, startBlue / 255f, 1),
- new Color4(endRed / 255f, endGreen / 255f, endBlue / 255f, 1));
- }
- break;
- case "P":
- {
- var type = split[4];
- switch (type)
- {
- case "A": timelineGroup?.BlendingMode.Add(easing, startTime, endTime, BlendingMode.Additive, startTime == endTime ? BlendingMode.Additive : BlendingMode.Inherit); break;
- case "H": timelineGroup?.FlipH.Add(easing, startTime, endTime, true, startTime == endTime); break;
- case "V": timelineGroup?.FlipV.Add(easing, startTime, endTime, true, startTime == endTime); break;
- }
- }
- break;
- default:
- throw new InvalidDataException($@"Unknown command type: {commandType}");
- }
- }
- break;
- }
- }
- }
-
- private static string cleanFilename(string path)
- => FileSafety.PathStandardise(path.Trim('\"'));
-
- private static Anchor parseOrigin(string value)
- {
- var origin = (LegacyOrigins)Enum.Parse(typeof(LegacyOrigins), value);
- switch (origin)
- {
- case LegacyOrigins.TopLeft: return Anchor.TopLeft;
- case LegacyOrigins.TopCentre: return Anchor.TopCentre;
- case LegacyOrigins.TopRight: return Anchor.TopRight;
- case LegacyOrigins.CentreLeft: return Anchor.CentreLeft;
- case LegacyOrigins.Centre: return Anchor.Centre;
- case LegacyOrigins.CentreRight: return Anchor.CentreRight;
- case LegacyOrigins.BottomLeft: return Anchor.BottomLeft;
- case LegacyOrigins.BottomCentre: return Anchor.BottomCentre;
- case LegacyOrigins.BottomRight: return Anchor.BottomRight;
- }
- throw new InvalidDataException($@"Unknown origin: {value}");
- }
-
- private static string parseLayer(string value)
- => Enum.Parse(typeof(StoryLayer), value).ToString();
-
- private void handleTimingPoints(Beatmap beatmap, string line)
- {
- if (beatmap == null)
- throw new ArgumentNullException(nameof(beatmap));
- if (line == null)
- throw new ArgumentNullException(nameof(line));
-
- string[] split = line.Split(',');
-
- double time = double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo);
- double beatLength = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo);
- double speedMultiplier = beatLength < 0 ? 100.0 / -beatLength : 1;
-
- TimeSignatures timeSignature = TimeSignatures.SimpleQuadruple;
- if (split.Length >= 3)
- timeSignature = split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)int.Parse(split[2]);
-
- LegacySampleBank sampleSet = defaultSampleBank;
- if (split.Length >= 4)
- sampleSet = (LegacySampleBank)int.Parse(split[3]);
-
- //SampleBank sampleBank = SampleBank.Default;
- //if (split.Length >= 5)
- // sampleBank = (SampleBank)int.Parse(split[4]);
-
- int sampleVolume = defaultSampleVolume;
- if (split.Length >= 6)
- sampleVolume = int.Parse(split[5]);
-
- bool timingChange = true;
- if (split.Length >= 7)
- timingChange = split[6][0] == '1';
-
- bool kiaiMode = false;
- bool omitFirstBarSignature = false;
- if (split.Length >= 8)
- {
- int effectFlags = int.Parse(split[7]);
- kiaiMode = (effectFlags & 1) > 0;
- omitFirstBarSignature = (effectFlags & 8) > 0;
- }
-
- string stringSampleSet = sampleSet.ToString().ToLower();
- if (stringSampleSet == @"none")
- stringSampleSet = @"normal";
-
- DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(time);
- SoundControlPoint soundPoint = beatmap.ControlPointInfo.SoundPointAt(time);
- EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(time);
-
- if (timingChange)
- {
- beatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint
- {
- Time = time,
- BeatLength = beatLength,
- TimeSignature = timeSignature
- });
- }
-
- if (speedMultiplier != difficultyPoint.SpeedMultiplier)
- {
- beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == time);
- beatmap.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint
- {
- Time = time,
- SpeedMultiplier = speedMultiplier
- });
- }
-
- if (stringSampleSet != soundPoint.SampleBank || sampleVolume != soundPoint.SampleVolume)
- {
- beatmap.ControlPointInfo.SoundPoints.Add(new SoundControlPoint
- {
- Time = time,
- SampleBank = stringSampleSet,
- SampleVolume = sampleVolume
- });
- }
-
- if (kiaiMode != effectPoint.KiaiMode || omitFirstBarSignature != effectPoint.OmitFirstBarLine)
- {
- beatmap.ControlPointInfo.EffectPoints.Add(new EffectControlPoint
- {
- Time = time,
- KiaiMode = kiaiMode,
- OmitFirstBarLine = omitFirstBarSignature
- });
- }
- }
-
- private void handleColours(Beatmap beatmap, string line, ref bool hasCustomColours)
- {
- if (beatmap == null)
- throw new ArgumentNullException(nameof(beatmap));
- if (line == null)
- throw new ArgumentNullException(nameof(line));
-
- var pair = splitKeyVal(line, ':');
-
- string[] split = pair.Value.Split(',');
-
- if (split.Length != 3)
- throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {pair.Value}");
-
- byte r, g, b;
- if (!byte.TryParse(split[0], out r) || !byte.TryParse(split[1], out g) || !byte.TryParse(split[2], out b))
- throw new InvalidOperationException(@"Color must be specified with 8-bit integer components");
-
- if (!hasCustomColours)
- {
- beatmap.ComboColors.Clear();
- hasCustomColours = true;
- }
-
- // Note: the combo index specified in the beatmap is discarded
- if (pair.Key.StartsWith(@"Combo"))
- {
- beatmap.ComboColors.Add(new Color4
- {
- R = r / 255f,
- G = g / 255f,
- B = b / 255f,
- A = 1f,
- });
- }
- }
-
- private void handleVariables(string line)
- {
- if (line == null)
- throw new ArgumentNullException(nameof(line));
-
- var pair = splitKeyVal(line, '=');
- variables[pair.Key] = pair.Value;
- }
-
- protected override Beatmap ParseFile(StreamReader stream)
- {
- return new LegacyBeatmap(base.ParseFile(stream));
- }
-
- public override Beatmap Decode(StreamReader stream)
- {
- return new LegacyBeatmap(base.Decode(stream));
- }
-
- protected override void ParseFile(StreamReader stream, Beatmap beatmap)
- {
- if (beatmap == null)
- throw new ArgumentNullException(nameof(beatmap));
- if (stream == null)
- throw new ArgumentNullException(nameof(stream));
-
- beatmap.BeatmapInfo.BeatmapVersion = beatmapVersion;
-
- Section section = Section.None;
- bool hasCustomColours = false;
- StoryboardSprite storyboardSprite = null;
- CommandTimelineGroup timelineGroup = null;
-
- string line;
- while ((line = stream.ReadLine()) != null)
- {
- if (string.IsNullOrWhiteSpace(line))
- continue;
-
- if (line.StartsWith("//"))
- continue;
-
- if (line.StartsWith(@"osu file format v"))
- {
- beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17));
- continue;
- }
-
- if (line.StartsWith(@"[") && line.EndsWith(@"]"))
- {
- if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section))
- throw new InvalidDataException($@"Unknown osu section {line}");
- continue;
- }
-
- switch (section)
- {
- case Section.General:
- handleGeneral(beatmap, line);
- break;
- case Section.Editor:
- handleEditor(beatmap, line);
- break;
- case Section.Metadata:
- handleMetadata(beatmap, line);
- break;
- case Section.Difficulty:
- handleDifficulty(beatmap, line);
- break;
- case Section.Events:
- handleEvents(beatmap, line, ref storyboardSprite, ref timelineGroup);
- break;
- case Section.TimingPoints:
- handleTimingPoints(beatmap, line);
- break;
- case Section.Colours:
- handleColours(beatmap, line, ref hasCustomColours);
- break;
- case Section.HitObjects:
-
- // If the ruleset wasn't specified, assume the osu!standard ruleset.
- if (parser == null)
- parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser();
-
- var obj = parser.Parse(line);
-
- if (obj != null)
- beatmap.HitObjects.Add(obj);
-
- break;
- case Section.Variables:
- handleVariables(line);
- break;
- }
- }
-
- foreach (var hitObject in beatmap.HitObjects)
- hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty);
- }
-
- private KeyValuePair splitKeyVal(string line, char separator)
- {
- if (line == null)
- throw new ArgumentNullException(nameof(line));
-
- var split = line.Trim().Split(new[] { separator }, 2);
-
- return new KeyValuePair
- (
- split[0].Trim(),
- split.Length > 1 ? split[1].Trim() : string.Empty
- );
- }
-
- internal enum LegacySampleBank
- {
- None = 0,
- Normal = 1,
- Soft = 2,
- Drum = 3
- }
-
- internal enum EventType
- {
- Background = 0,
- Video = 1,
- Break = 2,
- Colour = 3,
- Sprite = 4,
- Sample = 5,
- Animation = 6
- }
-
- internal enum LegacyOrigins
- {
- TopLeft,
- Centre,
- CentreLeft,
- TopRight,
- BottomCentre,
- TopCentre,
- Custom,
- CentreRight,
- BottomLeft,
- BottomRight
- };
-
- internal enum StoryLayer
- {
- Background = 0,
- Fail = 1,
- Pass = 2,
- Foreground = 3
- }
- }
-}
diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs
index 2a8178882e..736cc2a0b0 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap.cs
@@ -9,6 +9,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using osu.Game.Storyboards;
namespace osu.Game.Beatmaps
{
@@ -31,17 +32,19 @@ namespace osu.Game.Beatmaps
Mods.ValueChanged += mods => applyRateAdjustments();
beatmap = new AsyncLazy(populateBeatmap);
- background = new AsyncLazy(populateBackground);
+ background = new AsyncLazy(populateBackground, b => b == null || !b.IsDisposed);
track = new AsyncLazy
/// The type of the custom action.
- public abstract class DatabasedKeyBindingInputManager : KeyBindingInputManager
+ public class DatabasedKeyBindingInputManager : KeyBindingContainer
where T : struct
{
private readonly RulesetInfo ruleset;
@@ -31,7 +31,7 @@ namespace osu.Game.Input.Bindings
/// A reference to identify the current . Used to lookup mappings. Null for global mappings.
/// An optional variant for the specified . Used when a ruleset has more than one possible keyboard layouts.
/// Specify how to deal with multiple matches of s and s.
- protected DatabasedKeyBindingInputManager(RulesetInfo ruleset = null, int? variant = null, SimultaneousBindingMode simultaneousMode = SimultaneousBindingMode.None)
+ public DatabasedKeyBindingInputManager(RulesetInfo ruleset = null, int? variant = null, SimultaneousBindingMode simultaneousMode = SimultaneousBindingMode.None)
: base(simultaneousMode)
{
this.ruleset = ruleset;
diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs
index 95791391f0..9e2988417a 100644
--- a/osu.Game/Input/KeyBindingStore.cs
+++ b/osu.Game/Input/KeyBindingStore.cs
@@ -27,7 +27,7 @@ namespace osu.Game.Input
}
}
- public void Register(KeyBindingInputManager manager) => insertDefaults(manager.DefaultKeyBindings);
+ public void Register(KeyBindingContainer manager) => insertDefaults(manager.DefaultKeyBindings);
private void insertDefaults(IEnumerable defaults, int? rulesetId = null, int? variant = null)
{
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index 8eaa20f781..0ddff5e5aa 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -154,7 +154,7 @@ namespace osu.Game
Debug.Assert(lastBeatmap != null);
Debug.Assert(lastBeatmap.Track != null);
- lastBeatmap.DisposeTrack();
+ lastBeatmap.RecycleTrack();
}
Audio.Track.AddItem(b.Track);
diff --git a/osu.Game/Overlays/BeatmapSet/DownloadButton.cs b/osu.Game/Overlays/BeatmapSet/DownloadButton.cs
index 18a0cfd968..47787d2ced 100644
--- a/osu.Game/Overlays/BeatmapSet/DownloadButton.cs
+++ b/osu.Game/Overlays/BeatmapSet/DownloadButton.cs
@@ -14,10 +14,10 @@ namespace osu.Game.Overlays.BeatmapSet
public DownloadButton(string title, string subtitle)
{
Width = 120;
- RelativeSizeAxes = Axes.Y;
- Child = new Container
+ Add(new Container
{
+ Depth = -1,
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = 10 },
Children = new Drawable[]
@@ -53,7 +53,7 @@ namespace osu.Game.Overlays.BeatmapSet
Margin = new MarginPadding { Right = 5 },
},
},
- };
+ });
}
}
}
diff --git a/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs
index 9fd4ac177c..1b22853656 100644
--- a/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs
+++ b/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -15,13 +16,12 @@ namespace osu.Game.Overlays.BeatmapSet
{
public readonly Bindable Favourited = new Bindable();
- public FavouriteButton()
+ [BackgroundDependencyLoader]
+ private void load()
{
- RelativeSizeAxes = Axes.Y;
-
Container pink;
SpriteIcon icon;
- Children = new Drawable[]
+ AddRange(new Drawable[]
{
pink = new Container
{
@@ -51,7 +51,7 @@ namespace osu.Game.Overlays.BeatmapSet
Size = new Vector2(18),
Shadow = false,
},
- };
+ });
Favourited.ValueChanged += value =>
{
diff --git a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs
index 3075020fe6..ac5683de00 100644
--- a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs
+++ b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs
@@ -2,44 +2,27 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
-using osu.Game.Graphics.Backgrounds;
-using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.UserInterface;
+using osu.Framework.Allocation;
namespace osu.Game.Overlays.BeatmapSet
{
- public class HeaderButton : OsuClickableContainer
+ public class HeaderButton : TriangleButton
{
- private readonly Container content;
-
- protected override Container Content => content;
-
public HeaderButton()
{
- CornerRadius = 3;
- Masking = true;
+ Height = 0;
+ RelativeSizeAxes = Axes.Y;
+ }
- InternalChildren = new Drawable[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = OsuColour.FromHex(@"094c5f"),
- },
- new Triangles
- {
- RelativeSizeAxes = Axes.Both,
- ColourLight = OsuColour.FromHex(@"0f7c9b"),
- ColourDark = OsuColour.FromHex(@"094c5f"),
- TriangleScale = 1.5f,
- },
- content = new Container
- {
- RelativeSizeAxes = Axes.Both,
- },
- };
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ BackgroundColour = OsuColour.FromHex(@"094c5f");
+ Triangles.ColourLight = OsuColour.FromHex(@"0f7c9b");
+ Triangles.ColourDark = OsuColour.FromHex(@"094c5f");
+ Triangles.TriangleScale = 1.5f;
}
}
}
diff --git a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs
index 8ebd4ac545..4a7e4f4e6e 100644
--- a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs
+++ b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Overlays.KeyBinding
public override FontAwesome Icon => FontAwesome.fa_osu_hot;
public override string Header => "Global";
- public GlobalKeyBindingsSection(KeyBindingInputManager manager)
+ public GlobalKeyBindingsSection(KeyBindingContainer manager)
{
Add(new DefaultBindingsSubsection(manager));
}
@@ -21,7 +21,7 @@ namespace osu.Game.Overlays.KeyBinding
{
protected override string Header => string.Empty;
- public DefaultBindingsSubsection(KeyBindingInputManager manager)
+ public DefaultBindingsSubsection(KeyBindingContainer manager)
: base(null)
{
Defaults = manager.DefaultKeyBindings;
diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs
index af01cdc451..245b2d36ce 100644
--- a/osu.Game/Overlays/Music/PlaylistList.cs
+++ b/osu.Game/Overlays/Music/PlaylistList.cs
@@ -35,7 +35,11 @@ namespace osu.Game.Overlays.Music
set { base.Padding = value; }
}
- public IEnumerable BeatmapSets { set { items.Sets = value; } }
+ public IEnumerable BeatmapSets
+ {
+ get { return items.Sets; }
+ set { items.Sets = value; }
+ }
public BeatmapSetInfo FirstVisibleSet => items.FirstVisibleSet;
public BeatmapSetInfo NextSet => items.NextSet;
@@ -48,7 +52,7 @@ namespace osu.Game.Overlays.Music
}
public void AddBeatmapSet(BeatmapSetInfo beatmapSet) => items.AddBeatmapSet(beatmapSet);
- public bool RemoveBeatmapSet(BeatmapSetInfo beatmapSet) => items.RemoveBeatmapSet(beatmapSet);
+ public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) => items.RemoveBeatmapSet(beatmapSet);
public void Filter(string searchTerm) => items.SearchTerm = searchTerm;
@@ -81,6 +85,7 @@ namespace osu.Game.Overlays.Music
public IEnumerable Sets
{
+ get { return items.Select(x => x.BeatmapSetInfo).ToList(); }
set
{
items.Clear();
@@ -103,12 +108,11 @@ namespace osu.Game.Overlays.Music
});
}
- public bool RemoveBeatmapSet(BeatmapSetInfo beatmapSet)
+ public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet)
{
var itemToRemove = items.FirstOrDefault(i => i.BeatmapSetInfo.ID == beatmapSet.ID);
- if (itemToRemove == null)
- return false;
- return items.Remove(itemToRemove);
+ if (itemToRemove != null)
+ items.Remove(itemToRemove);
}
public BeatmapSetInfo SelectedSet
@@ -230,6 +234,7 @@ namespace osu.Game.Overlays.Music
private class ItemSearchContainer : FillFlowContainer, IHasFilterableChildren
{
public IEnumerable FilterTerms => new string[] { };
+
public bool MatchingFilter
{
set
diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs
index d05ad85726..23bec53014 100644
--- a/osu.Game/Overlays/Music/PlaylistOverlay.cs
+++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs
@@ -13,6 +13,7 @@ using osu.Game.Beatmaps;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
+using System.Threading;
namespace osu.Game.Overlays.Music
{
@@ -29,7 +30,7 @@ namespace osu.Game.Overlays.Music
private readonly Bindable beatmapBacking = new Bindable();
- public IEnumerable BeatmapSets;
+ public IEnumerable BeatmapSets => list.BeatmapSets;
[BackgroundDependencyLoader]
private void load(OsuGameBase game, BeatmapManager beatmaps, OsuColour colours)
@@ -74,11 +75,10 @@ namespace osu.Game.Overlays.Music
},
};
- beatmaps.BeatmapSetAdded += s => Schedule(() => list.AddBeatmapSet(s));
- beatmaps.BeatmapSetRemoved += s => Schedule(() => list.RemoveBeatmapSet(s));
-
- list.BeatmapSets = BeatmapSets = beatmaps.GetAllUsableBeatmapSets();
+ beatmaps.BeatmapSetAdded += list.AddBeatmapSet;
+ beatmaps.BeatmapSetRemoved += list.RemoveBeatmapSet;
+ list.BeatmapSets = beatmaps.GetAllUsableBeatmapSets();
beatmapBacking.BindTo(game.Beatmap);
@@ -121,7 +121,7 @@ namespace osu.Game.Overlays.Music
return;
}
- playSpecified(set.Beatmaps[0]);
+ playSpecified(set.Beatmaps.First());
}
public void PlayPrevious()
@@ -130,7 +130,7 @@ namespace osu.Game.Overlays.Music
if (playable != null)
{
- playSpecified(playable.Beatmaps[0]);
+ playSpecified(playable.Beatmaps.First());
list.SelectedSet = playable;
}
}
@@ -141,7 +141,7 @@ namespace osu.Game.Overlays.Music
if (playable != null)
{
- playSpecified(playable.Beatmaps[0]);
+ playSpecified(playable.Beatmaps.First());
list.SelectedSet = playable;
}
}
@@ -149,7 +149,15 @@ namespace osu.Game.Overlays.Music
private void playSpecified(BeatmapInfo info)
{
beatmapBacking.Value = beatmaps.GetWorkingBeatmap(info, beatmapBacking);
- beatmapBacking.Value.Track.Start();
+
+ var track = beatmapBacking.Value.Track;
+
+ track.Restart();
+
+ // this is temporary until we have blocking (async.Wait()) audio component methods.
+ // then we can call RestartAsync().Wait() or the blocking version above.
+ while (!track.IsRunning)
+ Thread.Sleep(1);
}
}
diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs
index 4f57ea1bcd..b30ee8f6fc 100644
--- a/osu.Game/Overlays/MusicController.cs
+++ b/osu.Game/Overlays/MusicController.cs
@@ -251,7 +251,7 @@ namespace osu.Game.Overlays
playButton.Icon = track.IsRunning ? FontAwesome.fa_pause_circle_o : FontAwesome.fa_play_circle_o;
- if (track.HasCompleted && !track.Looping && !beatmapBacking.Disabled)
+ if (track.HasCompleted && !beatmapBacking.Disabled && playlist.BeatmapSets.Any())
next();
}
else
diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
index 5ebac37cc8..392bc6f1bd 100644
--- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
@@ -97,6 +97,11 @@ namespace osu.Game.Overlays.Settings.Sections.Input
doubleValue.ValueChanged += newValue => base.Bindable.Value = newValue;
}
}
+
+ public SensitivitySetting()
+ {
+ KeyboardStep = 0.01f;
+ }
}
private class SensitivitySlider : OsuSliderBar
@@ -105,8 +110,6 @@ namespace osu.Game.Overlays.Settings.Sections.Input
public SensitivitySlider()
{
- KeyboardStep = 0.01f;
-
Current.ValueChanged += newValue =>
{
if (!isDragging && Sensitivity != null)
@@ -133,4 +136,4 @@ namespace osu.Game.Overlays.Settings.Sections.Input
public override string TooltipText => Current.Disabled ? "Enable raw input to adjust sensitivity" : Current.Value.ToString(@"0.##x");
}
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index 41958df29b..3184b84e98 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -77,9 +77,7 @@ namespace osu.Game.Rulesets.Edit
Alpha = 0,
AlwaysPresent = true,
},
- CreateUnderlay(),
- rulesetContainer,
- CreateOverlay(rulesetContainer.Playfield)
+ rulesetContainer
}
}
},
@@ -106,10 +104,6 @@ namespace osu.Game.Rulesets.Edit
protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true);
- protected virtual PlayfieldUnderlay CreateUnderlay() => new PlayfieldUnderlay();
-
- protected virtual PlayfieldOverlay CreateOverlay(Playfield playfield) => new PlayfieldOverlay(playfield);
-
protected abstract IReadOnlyList CompositionTools { get; }
}
}
diff --git a/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs b/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs
deleted file mode 100644
index bace5258f8..0000000000
--- a/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-
-namespace osu.Game.Rulesets.Edit
-{
- public class PlayfieldUnderlay : CompositeDrawable
- {
- public PlayfieldUnderlay()
- {
- RelativeSizeAxes = Axes.Both;
- }
- }
-}
diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
index d4f9c7191a..0d7d617405 100644
--- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
@@ -177,8 +177,8 @@ namespace osu.Game.Rulesets.Objects.Legacy
string[] split = str.Split(':');
- var bank = (OsuLegacyDecoder.LegacySampleBank)Convert.ToInt32(split[0]);
- var addbank = (OsuLegacyDecoder.LegacySampleBank)Convert.ToInt32(split[1]);
+ var bank = (LegacyDecoder.LegacySampleBank)Convert.ToInt32(split[0]);
+ var addbank = (LegacyDecoder.LegacySampleBank)Convert.ToInt32(split[1]);
// Let's not implement this for now, because this doesn't fit nicely into the bank structure
//string sampleFile = split2.Length > 4 ? split2[4] : string.Empty;
diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs
index 8c4d6de1fe..5cd79cff29 100644
--- a/osu.Game/Rulesets/UI/RulesetInputManager.cs
+++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs
@@ -5,6 +5,8 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
using osu.Framework.Timing;
@@ -16,11 +18,24 @@ using OpenTK.Input;
namespace osu.Game.Rulesets.UI
{
- public abstract class RulesetInputManager : DatabasedKeyBindingInputManager, ICanAttachKeyCounter, IHasReplayHandler
+ public abstract class RulesetInputManager : PassThroughInputManager, ICanAttachKeyCounter, IHasReplayHandler
where T : struct
{
- protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) : base(ruleset, variant, unique)
+ public class RulesetKeyBindingContainer : DatabasedKeyBindingInputManager
{
+ public RulesetKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
+ : base(ruleset, variant, unique)
+ {
+ }
+ }
+
+ protected readonly KeyBindingContainer KeyBindingContainer;
+
+ protected override Container Content => KeyBindingContainer;
+
+ protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
+ {
+ InternalChild = KeyBindingContainer = new RulesetKeyBindingContainer(ruleset, variant, unique);
}
#region Action mapping (for replays)
@@ -41,10 +56,10 @@ namespace osu.Game.Rulesets.UI
List newActions = replayState.PressedActions;
foreach (var released in lastPressedActions.Except(newActions))
- PropagateReleased(KeyBindingInputQueue, released);
+ KeyBindingContainer.TriggerReleased(released);
foreach (var pressed in newActions.Except(lastPressedActions))
- PropagatePressed(KeyBindingInputQueue, pressed);
+ KeyBindingContainer.TriggerPressed(pressed);
lastPressedActions = newActions;
}
@@ -203,7 +218,7 @@ namespace osu.Game.Rulesets.UI
Add(receptor);
keyCounter.SetReceptor(receptor);
- keyCounter.AddRange(DefaultKeyBindings.Select(b => b.GetAction()).Distinct().Select(b => new KeyCounterAction(b)));
+ keyCounter.AddRange(KeyBindingContainer.DefaultKeyBindings.Select(b => b.GetAction()).Distinct().Select(b => new KeyCounterAction(b)));
}
public class ActionReceptor : KeyCounterCollection.Receptor, IKeyBindingHandler
diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs
index 229d06ef09..df95a5c384 100644
--- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs
+++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs
@@ -40,7 +40,8 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
return;
}
- timeline.RelativeChildSize = new Vector2((float)Math.Max(1, Beatmap.Value.Track.Length), 1);
+ // Todo: This should be handled more gracefully
+ timeline.RelativeChildSize = Beatmap.Value.Track.Length == double.PositiveInfinity ? Vector2.One : new Vector2((float)Math.Max(1, Beatmap.Value.Track.Length), 1);
}
protected void Add(Drawable visualisation) => timeline.Add(visualisation);
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index bc86c683c7..607ff792d8 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -154,7 +154,6 @@ namespace osu.Game.Screens.Edit
}
currentScreen.Beatmap.BindTo(Beatmap);
- currentScreen.ExitRequested = Exit;
screenContainer.Add(currentScreen);
}
diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs
index 48cc7f3379..10b6c07f3d 100644
--- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs
+++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs
@@ -19,16 +19,16 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons
{
public class DrawableRadioButton : TriangleButton
{
- private static readonly Color4 default_background_colour = OsuColour.FromHex("333");
- private static readonly Color4 default_bubble_colour = default_background_colour.Darken(0.5f);
- private static readonly Color4 selected_background_colour = OsuColour.FromHex("1188aa");
- private static readonly Color4 selected_bubble_colour = selected_background_colour.Lighten(0.5f);
-
///
/// Invoked when this has been selected.
///
public Action Selected;
+ private Color4 defaultBackgroundColour;
+ private Color4 defaultBubbleColour;
+ private Color4 selectedBackgroundColour;
+ private Color4 selectedBubbleColour;
+
private readonly Drawable bubble;
private readonly RadioButton button;
@@ -50,17 +50,20 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons
Scale = new Vector2(0.5f),
X = 10,
Masking = true,
- Colour = default_bubble_colour,
Blending = BlendingMode.Additive,
Child = new Box { RelativeSizeAxes = Axes.Both }
};
}
[BackgroundDependencyLoader]
- private void load()
+ private void load(OsuColour colours)
{
+ defaultBackgroundColour = colours.Gray3;
+ defaultBubbleColour = defaultBackgroundColour.Darken(0.5f);
+ selectedBackgroundColour = colours.BlueDark;
+ selectedBubbleColour = selectedBackgroundColour.Lighten(0.5f);
+
Triangles.Alpha = 0;
- BackgroundColour = default_background_colour;
Content.EdgeEffect = new EdgeEffectParameters
{
@@ -92,8 +95,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons
if (!IsLoaded)
return;
- BackgroundColour = button.Selected ? selected_background_colour : default_background_colour;
- bubble.Colour = button.Selected ? selected_bubble_colour : default_bubble_colour;
+ BackgroundColour = button.Selected ? selectedBackgroundColour : defaultBackgroundColour;
+ bubble.Colour = button.Selected ? selectedBubbleColour : defaultBubbleColour;
}
protected override bool OnClick(InputState state)
diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs
index 08473eaeba..5f1def4a2e 100644
--- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs
+++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs
@@ -22,7 +22,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons
items = value;
buttonContainer.Clear();
- value.ForEach(addButton);
+ items.ForEach(addButton);
}
}
diff --git a/osu.Game/Screens/Edit/Screens/EditorScreen.cs b/osu.Game/Screens/Edit/Screens/EditorScreen.cs
index 9a158d20f1..ac248930d8 100644
--- a/osu.Game/Screens/Edit/Screens/EditorScreen.cs
+++ b/osu.Game/Screens/Edit/Screens/EditorScreen.cs
@@ -1,7 +1,6 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -11,8 +10,6 @@ namespace osu.Game.Screens.Edit.Screens
{
public class EditorScreen : Container
{
- public Action ExitRequested;
-
public readonly Bindable Beatmap = new Bindable();
protected override Container Content => content;
diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs
index ca541ea552..ec2e8e0cb1 100644
--- a/osu.Game/Screens/Loader.cs
+++ b/osu.Game/Screens/Loader.cs
@@ -1,8 +1,12 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
+using System.Collections.Generic;
+using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Shaders;
using osu.Game.Screens.Menu;
using OpenTK;
using osu.Framework.Screens;
@@ -33,14 +37,28 @@ namespace osu.Game.Screens
logo.FadeInFromZero(5000, Easing.OutQuint);
}
+ private OsuScreen loadScreen;
+ private ShaderPrecompiler precompiler;
+
protected override void OnEntering(Screen last)
{
base.OnEntering(last);
- if (showDisclaimer)
- LoadComponentAsync(new Disclaimer(), d => Push(d));
- else
- LoadComponentAsync(new Intro(), d => Push(d));
+ LoadComponentAsync(precompiler = new ShaderPrecompiler(loadIfReady), Add);
+ LoadComponentAsync(loadScreen = showDisclaimer ? (OsuScreen)new Disclaimer() : new Intro(), s => loadIfReady());
+ }
+
+ private void loadIfReady()
+ {
+ if (ChildScreen == loadScreen) return;
+
+ if (loadScreen.LoadState != LoadState.Ready)
+ return;
+
+ if (!precompiler.FinishedCompiling)
+ return;
+
+ Push(loadScreen);
}
protected override void LogoSuspending(OsuLogo logo)
@@ -54,5 +72,49 @@ namespace osu.Game.Screens
{
showDisclaimer = game.IsDeployedBuild;
}
+
+ ///
+ /// Compiles a set of shaders before continuing. Attempts to draw some frames between compilation by limiting to one compile per draw frame.
+ ///
+ public class ShaderPrecompiler : Drawable
+ {
+ private readonly Action onLoaded;
+ private readonly List loadTargets = new List();
+
+ public bool FinishedCompiling { get; private set; }
+
+ public ShaderPrecompiler(Action onLoaded)
+ {
+ this.onLoaded = onLoaded;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(ShaderManager manager)
+ {
+ loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED));
+ loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.BLUR));
+ loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE));
+
+ loadTargets.Add(manager.Load(@"CursorTrail", FragmentShaderDescriptor.TEXTURE));
+
+ loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE_ROUNDED));
+ loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE));
+ }
+
+ private Shader currentLoadTarget;
+
+ protected override void Update()
+ {
+ base.Update();
+
+ // if our target is null we are done.
+ if (loadTargets.All(s => s.Loaded))
+ {
+ FinishedCompiling = true;
+ Expire();
+ onLoaded?.Invoke();
+ }
+ }
+ }
}
}
diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs
index 76ee4a607e..4a27c7f1ea 100644
--- a/osu.Game/Screens/OsuScreen.cs
+++ b/osu.Game/Screens/OsuScreen.cs
@@ -13,6 +13,8 @@ using osu.Framework.Audio;
using osu.Framework.Graphics;
using osu.Game.Rulesets;
using osu.Game.Screens.Menu;
+using osu.Framework.Input;
+using OpenTK.Input;
namespace osu.Game.Screens
{
@@ -73,6 +75,20 @@ namespace osu.Game.Screens
sampleExit = audio.Sample.Get(@"UI/screen-back");
}
+ protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
+ {
+ if (args.Repeat || !IsCurrentScreen) return false;
+
+ switch (args.Key)
+ {
+ case Key.Escape:
+ Exit();
+ return true;
+ }
+
+ return base.OnKeyDown(state, args);
+ }
+
protected override void OnResuming(Screen last)
{
base.OnResuming(last);
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index dc746b305c..b5b09504da 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -245,7 +245,7 @@ namespace osu.Game.Screens.Play
private void initializeStoryboard(bool asyncLoad)
{
- var beatmap = Beatmap.Value.Beatmap;
+ var beatmap = Beatmap.Value;
storyboard = beatmap.Storyboard.CreateDrawable(Beatmap.Value);
storyboard.Masking = true;
@@ -388,7 +388,7 @@ namespace osu.Game.Screens.Play
initializeStoryboard(true);
var beatmap = Beatmap.Value;
- var storyboardVisible = showStoryboard && beatmap.Beatmap.Storyboard.HasDrawable;
+ var storyboardVisible = showStoryboard && beatmap.Storyboard.HasDrawable;
storyboardContainer.FadeColour(new Color4(opacity, opacity, opacity, 1), 800);
storyboardContainer.FadeTo(storyboardVisible && opacity > 0 ? 1 : 0);
diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs
index b0a636dfb3..3f42ae11ac 100644
--- a/osu.Game/Screens/Select/BeatmapCarousel.cs
+++ b/osu.Game/Screens/Select/BeatmapCarousel.cs
@@ -45,7 +45,7 @@ namespace osu.Game.Screens.Select
Task.Run(() =>
{
- newGroups = value.Select(createGroup).ToList();
+ newGroups = value.Select(createGroup).Where(g => g != null).ToList();
criteria.Filter(newGroups);
}).ContinueWith(t =>
{
@@ -124,16 +124,19 @@ namespace osu.Game.Screens.Select
// todo: this method should be smarter as to not recreate panels that haven't changed, etc.
var group = groups.Find(b => b.BeatmapSet.ID == set.ID);
- if (group == null)
- return;
-
int i = groups.IndexOf(group);
- groups.RemoveAt(i);
+ if (i >= 0)
+ groups.RemoveAt(i);
var newGroup = createGroup(set);
if (newGroup != null)
- groups.Insert(i, newGroup);
+ {
+ if (i >= 0)
+ groups.Insert(i, newGroup);
+ else
+ groups.Add(newGroup);
+ }
bool hadSelection = selectedGroup == group;
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index 6fcaff7976..46284226d7 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -263,10 +263,7 @@ namespace osu.Game.Screens.Select
beatmapNoDebounce = beatmap;
if (beatmap == null)
- {
- if (!Beatmap.IsDefault)
- performLoad();
- }
+ performLoad();
else
{
if (beatmap.BeatmapSetInfoID == beatmapNoDebounce?.BeatmapSetInfoID)
diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs
index 59cbe74650..4eca910c1e 100644
--- a/osu.Game/Storyboards/Storyboard.cs
+++ b/osu.Game/Storyboards/Storyboard.cs
@@ -5,10 +5,11 @@ using osu.Game.Beatmaps;
using osu.Game.Storyboards.Drawables;
using System.Collections.Generic;
using System.Linq;
+using System;
namespace osu.Game.Storyboards
{
- public class Storyboard
+ public class Storyboard : IDisposable
{
private readonly Dictionary layers = new Dictionary();
public IEnumerable Layers => layers.Values;
@@ -59,5 +60,29 @@ namespace osu.Game.Storyboards
}
return drawable;
}
+
+ #region Disposal
+
+ ~Storyboard()
+ {
+ Dispose(false);
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ private bool isDisposed;
+
+ protected virtual void Dispose(bool isDisposing)
+ {
+ if (isDisposed)
+ return;
+ isDisposed = true;
+ }
+
+ #endregion
}
}
diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs
index d9951e002b..106f0fa8f3 100644
--- a/osu.Game/Tests/Visual/TestCasePlayer.cs
+++ b/osu.Game/Tests/Visual/TestCasePlayer.cs
@@ -8,7 +8,6 @@ using System.Text;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
-using osu.Game.Beatmaps.Formats;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Play;
@@ -63,7 +62,7 @@ namespace osu.Game.Tests.Visual
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data)))
using (var reader = new StreamReader(stream))
- beatmap = BeatmapDecoder.GetDecoder(reader).Decode(reader);
+ beatmap = Game.Beatmaps.Formats.Decoder.GetDecoder(reader).DecodeBeatmap(reader);
return beatmap;
}
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index c12ecc8b14..660d7cb5e6 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -1,5 +1,6 @@
-
+
+
{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}
Debug
@@ -61,7 +62,6 @@
false
- 6
none
@@ -195,9 +195,6 @@
-
- osu.licenseheader
-
@@ -269,6 +266,8 @@
+
+
@@ -283,7 +282,7 @@
20171025071459_AddMissingIndexRules.cs
-
+
20171119065731_AddBeatmapOnlineIDUniqueConstraint.cs
@@ -302,9 +301,6 @@
-
-
-
@@ -313,8 +309,8 @@
-
-
+
+
diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings
index fdfbf25144..76929dcbb3 100644
--- a/osu.sln.DotSettings
+++ b/osu.sln.DotSettings
@@ -34,6 +34,7 @@
HINT
WARNING
WARNING
+ HINT
WARNING
WARNING
DO_NOT_SHOW
@@ -44,13 +45,16 @@
WARNING
ERROR
HINT
+ HINT
HINT
WARNING
WARNING
+ HINT
DO_NOT_SHOW
HINT
HINT
HINT
+ HINT
WARNING
WARNING
WARNING
@@ -149,6 +153,7 @@
WARNING
WARNING
WARNING
+ HINT
WARNING
WARNING
HINT