diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs new file mode 100644 index 0000000000..275de8121f --- /dev/null +++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs @@ -0,0 +1,46 @@ +using System; +using System.IO; +using NUnit.Framework; +using osu.Game.Beatmaps.IO; +using osu.Game.Tests.Resources; + +namespace osu.Game.Tests.Beatmaps.IO +{ + [TestFixture] + public class OszArchiveReaderTest + { + [TestFixtureSetUp] + public void SetUp() + { + OszArchiveReader.Register(); + } + + [Test] + public void TestReadBeatmaps() + { + using (var osz = File.OpenRead(Resource.GetPath("241526 Soleily - Renatus.osz"))) + { + var reader = new OszArchiveReader(osz); + string[] expected = + { + "Soleily - Renatus (Deif) [Platter].osu", + "Soleily - Renatus (Deif) [Rain].osu", + "Soleily - Renatus (Deif) [Salad].osu", + "Soleily - Renatus (ExPew) [Another].osu", + "Soleily - Renatus (ExPew) [Hyper].osu", + "Soleily - Renatus (ExPew) [Normal].osu", + "Soleily - Renatus (Gamu) [Hard].osu", + "Soleily - Renatus (Gamu) [Insane].osu", + "Soleily - Renatus (Gamu) [Normal].osu", + "Soleily - Renatus (MMzz) [Futsuu].osu", + "Soleily - Renatus (MMzz) [Muzukashii].osu", + "Soleily - Renatus (MMzz) [Oni].osu" + }; + var maps = reader.ReadBeatmaps(); + foreach (var map in expected) + Assert.Contains(map, maps); + } + } + } +} + diff --git a/osu.Game.Tests/Resources/Resource.cs b/osu.Game.Tests/Resources/Resource.cs new file mode 100644 index 0000000000..ccc136f2b3 --- /dev/null +++ b/osu.Game.Tests/Resources/Resource.cs @@ -0,0 +1,15 @@ +using System; +using System.IO; +using System.Reflection; + +namespace osu.Game.Tests.Resources +{ + public static class Resource + { + public static string GetPath(string path) + { + var assemblyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + return Path.Combine(assemblyDir, "Resources", path); + } + } +} \ No newline at end of file diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj new file mode 100644 index 0000000000..82f06ad84d --- /dev/null +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -0,0 +1,57 @@ + + + + Debug + AnyCPU + {54377672-20B1-40AF-8087-5CF73BF3953A} + Library + osu.Game.Tests + osu.Game.Tests + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + false + + + true + bin\Release + prompt + 4 + false + + + + + ..\packages\NUnit.2.6.4\lib\nunit.framework.dll + + + + + + PreserveNewest + + + + + {0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D} + osu.Game + + + + + + + + + + + + + \ No newline at end of file diff --git a/osu.Game.Tests/packages.config b/osu.Game.Tests/packages.config new file mode 100644 index 0000000000..c714ef3a23 --- /dev/null +++ b/osu.Game.Tests/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs index e5ca017f9f..879a6b0c34 100644 --- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs @@ -1,11 +1,15 @@ using System; +using System.Collections.Generic; using System.IO; +using osu.Game.Beatmaps.Objects; +using osu.Game.Beatmaps.Timing; +using osu.Game.GameModes.Play; namespace osu.Game.Beatmaps.Formats { public class OsuLegacyDecoder : BeatmapDecoder { - static OsuLegacyDecoder() + public static void Register() { AddDecoder("osu file format v14"); AddDecoder("osu file format v13"); @@ -16,6 +20,7 @@ namespace osu.Game.Beatmaps.Formats } private enum Section { + None, General, Editor, Metadata, @@ -25,10 +30,102 @@ namespace osu.Game.Beatmaps.Formats Colours, HitObjects, } + private void HandleGeneral(Beatmap beatmap, string key, string val) + { + switch (key) + { + case "AudioFilename": + beatmap.Metadata.AudioFile = val; + break; + case "AudioLeadIn": + // TODO + break; + case "PreviewTime": + beatmap.Metadata.PreviewTime = int.Parse(val); + break; + case "Countdown": + // TODO + break; + case "SampleSet": + // TODO + break; + case "StackLeniency": + // TODO + break; + case "Mode": + beatmap.Metadata.Mode = (PlayMode)int.Parse(val); + break; + case "LetterboxInBreaks": + // TODO + break; + case "SpecialStyle": + // TODO + break; + case "WidescreenStoryboard": + // TODO + break; + } + } public override Beatmap Decode(TextReader stream) - { - throw new NotImplementedException(); + { + var beatmap = new Beatmap + { + Metadata = new BeatmapMetadata(), + HitObjects = new List(), + ControlPoints = new List(), + }; + var section = Section.None; + string line; + while (true) + { + line = stream.ReadLine(); + if (line == null) + break; + line = line.Trim(); + if (string.IsNullOrEmpty(line)) + continue; + + if (line.StartsWith("[") && line.EndsWith("]")) + { + if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section)) + throw new InvalidOperationException($@"Unknown osu section {line}"); + continue; + } + + string val = line, key = null; + if (section != Section.Events && section != Section.TimingPoints + && section != Section.HitObjects) + { + key = val.Remove(val.IndexOf(':')).Trim(); + val = val.Substring(val.IndexOf(':') + 1).Trim(); + } + switch (section) + { + case Section.General: + HandleGeneral(beatmap, key, val); + break; + case Section.Editor: + // TODO + break; + case Section.Metadata: + // TODO + break; + case Section.Difficulty: + // TODO + break; + case Section.Events: + // TODO + break; + case Section.TimingPoints: + // TODO + break; + case Section.HitObjects: + // TODO + break; + } + } + return beatmap; } } } \ No newline at end of file diff --git a/osu.Game/Beatmaps/IO/OszArchiveReader.cs b/osu.Game/Beatmaps/IO/OszArchiveReader.cs index c7213cbedc..a63468c16e 100644 --- a/osu.Game/Beatmaps/IO/OszArchiveReader.cs +++ b/osu.Game/Beatmaps/IO/OszArchiveReader.cs @@ -6,9 +6,9 @@ using osu.Game.Beatmaps.Formats; namespace osu.Game.Beatmaps.IO { - public class OszArchiveReader : ArchiveReader + public sealed class OszArchiveReader : ArchiveReader { - static OszArchiveReader() + public static void Register() { AddReader((storage, path) => { @@ -20,6 +20,7 @@ namespace osu.Game.Beatmaps.IO return zip.Entries.Any(e => e.FileName.EndsWith(".osu")); } }); + OsuLegacyDecoder.Register(); } private ZipFile Archive { get; set; } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 89b5229ad9..ea2fecf7c2 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -5,6 +5,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; +using osu.Game.Beatmaps.IO; using osu.Game.Configuration; using osu.Game.Database; using osu.Game.Graphics.Cursor; @@ -34,6 +35,7 @@ namespace osu.Game { base.Load(game); + OszArchiveReader.Register(); Beatmaps = new BeatmapDatabase(Host.Storage); //this completely overrides the framework default. will need to change once we make a proper FontStore. diff --git a/osu.sln b/osu.sln index 1454c14c82..d9f58def79 100644 --- a/osu.sln +++ b/osu.sln @@ -19,6 +19,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework.Desktop", "os EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Desktop.VisualTests", "osu.Desktop.VisualTests\osu.Desktop.VisualTests.csproj", "{69051C69-12AE-4E7D-A3E6-460D2E282312}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Tests", "osu.Game.Tests\osu.Game.Tests.csproj", "{54377672-20B1-40AF-8087-5CF73BF3953A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -56,6 +58,12 @@ Global {69051C69-12AE-4E7D-A3E6-460D2E282312}.Deploy|Any CPU.ActiveCfg = Debug|Any CPU {69051C69-12AE-4E7D-A3E6-460D2E282312}.Release|Any CPU.ActiveCfg = Release|Any CPU {69051C69-12AE-4E7D-A3E6-460D2E282312}.Release|Any CPU.Build.0 = Release|Any CPU + {54377672-20B1-40AF-8087-5CF73BF3953A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {54377672-20B1-40AF-8087-5CF73BF3953A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {54377672-20B1-40AF-8087-5CF73BF3953A}.Deploy|Any CPU.ActiveCfg = Release|Any CPU + {54377672-20B1-40AF-8087-5CF73BF3953A}.Deploy|Any CPU.Build.0 = Release|Any CPU + {54377672-20B1-40AF-8087-5CF73BF3953A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {54377672-20B1-40AF-8087-5CF73BF3953A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -67,6 +75,7 @@ Global {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58} = {0D37A2AD-80A4-464F-A1DE-1560B70F1CE3} {65DC628F-A640-4111-AB35-3A5652BC1E17} = {7A75DFA2-DE65-4458-98AF-26AF96FFD6DC} {69051C69-12AE-4E7D-A3E6-460D2E282312} = {0D37A2AD-80A4-464F-A1DE-1560B70F1CE3} + {54377672-20B1-40AF-8087-5CF73BF3953A} = {0D37A2AD-80A4-464F-A1DE-1560B70F1CE3} EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution Policies = $0