2019-01-24 16:43:03 +08:00
|
|
|
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
|
|
|
// See the LICENCE file in the repository root for full licence text.
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2022-06-17 15:37:17 +08:00
|
|
|
|
#nullable disable
|
|
|
|
|
|
2018-04-13 17:19:50 +08:00
|
|
|
|
using System.Linq;
|
|
|
|
|
using NUnit.Framework;
|
2018-11-20 15:51:59 +08:00
|
|
|
|
using osuTK;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
using osu.Framework.Graphics;
|
|
|
|
|
using osu.Game.Beatmaps.Formats;
|
2019-09-10 06:43:30 +08:00
|
|
|
|
using osu.Game.IO;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
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 LegacyStoryboardDecoder();
|
2019-04-01 11:44:46 +08:00
|
|
|
|
|
2019-01-28 17:19:57 +08:00
|
|
|
|
using (var resStream = TestResources.OpenResource("Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu"))
|
2019-09-10 06:43:30 +08:00
|
|
|
|
using (var stream = new LineBufferedReader(resStream))
|
2018-04-13 17:19:50 +08:00
|
|
|
|
{
|
|
|
|
|
var storyboard = decoder.Decode(stream);
|
|
|
|
|
|
|
|
|
|
Assert.IsTrue(storyboard.HasDrawable);
|
2020-05-19 04:10:02 +08:00
|
|
|
|
Assert.AreEqual(6, storyboard.Layers.Count());
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
|
|
|
|
StoryboardLayer background = storyboard.Layers.FirstOrDefault(l => l.Depth == 3);
|
|
|
|
|
Assert.IsNotNull(background);
|
2019-03-26 15:14:20 +08:00
|
|
|
|
Assert.AreEqual(16, background.Elements.Count);
|
2020-03-25 10:08:08 +08:00
|
|
|
|
Assert.IsTrue(background.VisibleWhenFailing);
|
|
|
|
|
Assert.IsTrue(background.VisibleWhenPassing);
|
2018-04-13 17:19:50 +08:00
|
|
|
|
Assert.AreEqual("Background", background.Name);
|
|
|
|
|
|
|
|
|
|
StoryboardLayer fail = storyboard.Layers.FirstOrDefault(l => l.Depth == 2);
|
|
|
|
|
Assert.IsNotNull(fail);
|
2019-03-26 15:14:20 +08:00
|
|
|
|
Assert.AreEqual(0, fail.Elements.Count);
|
2020-03-25 10:08:08 +08:00
|
|
|
|
Assert.IsTrue(fail.VisibleWhenFailing);
|
|
|
|
|
Assert.IsFalse(fail.VisibleWhenPassing);
|
2018-04-13 17:19:50 +08:00
|
|
|
|
Assert.AreEqual("Fail", fail.Name);
|
|
|
|
|
|
|
|
|
|
StoryboardLayer pass = storyboard.Layers.FirstOrDefault(l => l.Depth == 1);
|
|
|
|
|
Assert.IsNotNull(pass);
|
2019-03-26 15:14:20 +08:00
|
|
|
|
Assert.AreEqual(0, pass.Elements.Count);
|
2020-03-25 10:08:08 +08:00
|
|
|
|
Assert.IsFalse(pass.VisibleWhenFailing);
|
|
|
|
|
Assert.IsTrue(pass.VisibleWhenPassing);
|
2018-04-13 17:19:50 +08:00
|
|
|
|
Assert.AreEqual("Pass", pass.Name);
|
|
|
|
|
|
|
|
|
|
StoryboardLayer foreground = storyboard.Layers.FirstOrDefault(l => l.Depth == 0);
|
|
|
|
|
Assert.IsNotNull(foreground);
|
2019-03-26 15:14:20 +08:00
|
|
|
|
Assert.AreEqual(151, foreground.Elements.Count);
|
2020-03-25 10:08:08 +08:00
|
|
|
|
Assert.IsTrue(foreground.VisibleWhenFailing);
|
|
|
|
|
Assert.IsTrue(foreground.VisibleWhenPassing);
|
2018-04-13 17:19:50 +08:00
|
|
|
|
Assert.AreEqual("Foreground", foreground.Name);
|
|
|
|
|
|
2020-05-19 04:10:02 +08:00
|
|
|
|
StoryboardLayer overlay = storyboard.Layers.FirstOrDefault(l => l.Depth == int.MinValue);
|
|
|
|
|
Assert.IsNotNull(overlay);
|
|
|
|
|
Assert.IsEmpty(overlay.Elements);
|
|
|
|
|
Assert.IsTrue(overlay.VisibleWhenFailing);
|
|
|
|
|
Assert.IsTrue(overlay.VisibleWhenPassing);
|
|
|
|
|
Assert.AreEqual("Overlay", overlay.Name);
|
|
|
|
|
|
2018-04-13 17:19:50 +08:00
|
|
|
|
int spriteCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSprite));
|
|
|
|
|
int animationCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardAnimation));
|
2019-08-23 20:18:56 +08:00
|
|
|
|
int sampleCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSampleInfo));
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
|
|
|
|
Assert.AreEqual(15, spriteCount);
|
|
|
|
|
Assert.AreEqual(1, animationCount);
|
|
|
|
|
Assert.AreEqual(0, sampleCount);
|
2019-03-26 15:14:20 +08:00
|
|
|
|
Assert.AreEqual(background.Elements.Count, spriteCount + animationCount + sampleCount);
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
|
|
|
|
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);
|
2019-12-20 15:42:45 +08:00
|
|
|
|
Assert.AreEqual("SB/lyric/ja-21.png", sprite.Path);
|
2018-04-13 17:19:50 +08:00
|
|
|
|
|
2019-03-26 15:14:20 +08:00
|
|
|
|
var animation = background.Elements.OfType<StoryboardAnimation>().First();
|
2018-04-13 17:19:50 +08:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-14 17:15:09 +08:00
|
|
|
|
|
2022-09-12 13:17:15 +08:00
|
|
|
|
[Test]
|
|
|
|
|
public void TestCorrectAnimationStartTime()
|
|
|
|
|
{
|
|
|
|
|
var decoder = new LegacyStoryboardDecoder();
|
|
|
|
|
|
|
|
|
|
using (var resStream = TestResources.OpenResource("animation-starts-before-alpha.osb"))
|
|
|
|
|
using (var stream = new LineBufferedReader(resStream))
|
|
|
|
|
{
|
|
|
|
|
var storyboard = decoder.Decode(stream);
|
|
|
|
|
|
|
|
|
|
StoryboardLayer background = storyboard.Layers.Single(l => l.Depth == 3);
|
|
|
|
|
Assert.AreEqual(1, background.Elements.Count);
|
|
|
|
|
|
|
|
|
|
Assert.AreEqual(2000, background.Elements[0].StartTime);
|
|
|
|
|
// This property should be used in DrawableStoryboardAnimation as a starting point for animation playback.
|
|
|
|
|
Assert.AreEqual(1000, (background.Elements[0] as StoryboardAnimation)?.EarliestTransformTime);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-04 14:14:39 +08:00
|
|
|
|
[Test]
|
|
|
|
|
public void TestOutOfOrderStartTimes()
|
|
|
|
|
{
|
|
|
|
|
var decoder = new LegacyStoryboardDecoder();
|
|
|
|
|
|
|
|
|
|
using (var resStream = TestResources.OpenResource("out-of-order-starttimes.osb"))
|
|
|
|
|
using (var stream = new LineBufferedReader(resStream))
|
|
|
|
|
{
|
|
|
|
|
var storyboard = decoder.Decode(stream);
|
|
|
|
|
|
|
|
|
|
StoryboardLayer background = storyboard.Layers.Single(l => l.Depth == 3);
|
|
|
|
|
Assert.AreEqual(2, background.Elements.Count);
|
|
|
|
|
|
|
|
|
|
Assert.AreEqual(1500, background.Elements[0].StartTime);
|
|
|
|
|
Assert.AreEqual(1000, background.Elements[1].StartTime);
|
|
|
|
|
|
2021-01-04 14:16:01 +08:00
|
|
|
|
Assert.AreEqual(1000, storyboard.EarliestEventTime);
|
2021-01-04 14:14:39 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-06 15:16:54 +08:00
|
|
|
|
[Test]
|
|
|
|
|
public void TestEarliestStartTimeWithLoopAlphas()
|
|
|
|
|
{
|
|
|
|
|
var decoder = new LegacyStoryboardDecoder();
|
|
|
|
|
|
|
|
|
|
using (var resStream = TestResources.OpenResource("loop-containing-earlier-non-zero-fade.osb"))
|
|
|
|
|
using (var stream = new LineBufferedReader(resStream))
|
|
|
|
|
{
|
|
|
|
|
var storyboard = decoder.Decode(stream);
|
|
|
|
|
|
|
|
|
|
StoryboardLayer background = storyboard.Layers.Single(l => l.Depth == 3);
|
2022-09-06 15:40:59 +08:00
|
|
|
|
Assert.AreEqual(2, background.Elements.Count);
|
|
|
|
|
|
2022-09-06 15:16:54 +08:00
|
|
|
|
Assert.AreEqual(1000, background.Elements[0].StartTime);
|
2022-09-06 15:40:59 +08:00
|
|
|
|
Assert.AreEqual(1000, background.Elements[1].StartTime);
|
|
|
|
|
|
2022-09-06 15:16:54 +08:00
|
|
|
|
Assert.AreEqual(1000, storyboard.EarliestEventTime);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-14 17:15:09 +08:00
|
|
|
|
[Test]
|
|
|
|
|
public void TestDecodeVariableWithSuffix()
|
|
|
|
|
{
|
|
|
|
|
var decoder = new LegacyStoryboardDecoder();
|
2019-04-01 11:44:46 +08:00
|
|
|
|
|
2019-01-28 17:19:57 +08:00
|
|
|
|
using (var resStream = TestResources.OpenResource("variable-with-suffix.osb"))
|
2019-09-10 06:43:30 +08:00
|
|
|
|
using (var stream = new LineBufferedReader(resStream))
|
2018-08-14 17:15:09 +08:00
|
|
|
|
{
|
|
|
|
|
var storyboard = decoder.Decode(stream);
|
|
|
|
|
|
|
|
|
|
StoryboardLayer background = storyboard.Layers.Single(l => l.Depth == 3);
|
2020-03-10 11:21:40 +08:00
|
|
|
|
Assert.AreEqual(3456, ((StoryboardSprite)background.Elements.Single()).InitialPosition.X);
|
2018-08-14 17:15:09 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-01-31 22:23:53 +08:00
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
|
public void TestDecodeOutOfRangeLoopAnimationType()
|
|
|
|
|
{
|
|
|
|
|
var decoder = new LegacyStoryboardDecoder();
|
|
|
|
|
|
|
|
|
|
using (var resStream = TestResources.OpenResource("animation-types.osb"))
|
|
|
|
|
using (var stream = new LineBufferedReader(resStream))
|
|
|
|
|
{
|
|
|
|
|
var storyboard = decoder.Decode(stream);
|
|
|
|
|
|
|
|
|
|
StoryboardLayer foreground = storyboard.Layers.Single(l => l.Depth == 0);
|
|
|
|
|
Assert.AreEqual(AnimationLoopType.LoopForever, ((StoryboardAnimation)foreground.Elements[0]).LoopType);
|
|
|
|
|
Assert.AreEqual(AnimationLoopType.LoopOnce, ((StoryboardAnimation)foreground.Elements[1]).LoopType);
|
|
|
|
|
Assert.AreEqual(AnimationLoopType.LoopForever, ((StoryboardAnimation)foreground.Elements[2]).LoopType);
|
|
|
|
|
Assert.AreEqual(AnimationLoopType.LoopOnce, ((StoryboardAnimation)foreground.Elements[3]).LoopType);
|
|
|
|
|
Assert.AreEqual(AnimationLoopType.LoopForever, ((StoryboardAnimation)foreground.Elements[4]).LoopType);
|
|
|
|
|
Assert.AreEqual(AnimationLoopType.LoopForever, ((StoryboardAnimation)foreground.Elements[5]).LoopType);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-10-03 00:40:41 +08:00
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
|
public void TestDecodeLoopCount()
|
|
|
|
|
{
|
|
|
|
|
// all loop sequences in loop-count.osb have a total duration of 2000ms (fade in 0->1000ms, fade out 1000->2000ms).
|
|
|
|
|
const double loop_duration = 2000;
|
|
|
|
|
|
|
|
|
|
var decoder = new LegacyStoryboardDecoder();
|
|
|
|
|
|
|
|
|
|
using (var resStream = TestResources.OpenResource("loop-count.osb"))
|
|
|
|
|
using (var stream = new LineBufferedReader(resStream))
|
|
|
|
|
{
|
|
|
|
|
var storyboard = decoder.Decode(stream);
|
|
|
|
|
|
|
|
|
|
StoryboardLayer background = storyboard.Layers.Single(l => l.Depth == 3);
|
|
|
|
|
|
|
|
|
|
// stable ensures that any loop command executes at least once, even if the loop count specified in the .osb is zero or negative.
|
|
|
|
|
StoryboardSprite zeroTimes = background.Elements.OfType<StoryboardSprite>().Single(s => s.Path == "zero-times.png");
|
|
|
|
|
Assert.That(zeroTimes.EndTime, Is.EqualTo(1000 + loop_duration));
|
|
|
|
|
|
|
|
|
|
StoryboardSprite oneTime = background.Elements.OfType<StoryboardSprite>().Single(s => s.Path == "one-time.png");
|
|
|
|
|
Assert.That(oneTime.EndTime, Is.EqualTo(4000 + loop_duration));
|
|
|
|
|
|
|
|
|
|
StoryboardSprite manyTimes = background.Elements.OfType<StoryboardSprite>().Single(s => s.Path == "many-times.png");
|
2023-02-06 15:39:44 +08:00
|
|
|
|
// It is intentional that we don't consider the loop count (40) as part of the end time calculation to match stable's handling.
|
|
|
|
|
// If we were to include the loop count, storyboards which loop for stupid long loop counts would continue playing the outro forever.
|
|
|
|
|
Assert.That(manyTimes.EndTime, Is.EqualTo(9000 + loop_duration));
|
2021-10-03 00:40:41 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-04-13 17:19:50 +08:00
|
|
|
|
}
|
|
|
|
|
}
|