1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-19 12:22:57 +08:00

Merge branch 'refs/heads/db-refactor' into general-fixes

This commit is contained in:
Dean Herbert 2016-10-19 12:22:04 +09:00
commit 0e9a155be5
17 changed files with 253 additions and 221 deletions

2
.vscode/launch.json vendored
View File

@ -5,7 +5,7 @@
"name": "Launch", "name": "Launch",
"type": "mono", "type": "mono",
"request": "launch", "request": "launch",
"program": "${workspaceRoot}/osu.Desktop.VisualTests/bin/Debug/osu!.exe", "program": "${workspaceRoot}/osu.Desktop/bin/Debug/osu!.exe",
"args": [], "args": [],
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": "", "preLaunchTask": "",

View File

@ -5,6 +5,7 @@ using System.Linq;
using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.IO; using osu.Game.Beatmaps.IO;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Database;
namespace osu.Desktop.Beatmaps.IO namespace osu.Desktop.Beatmaps.IO
{ {
@ -31,8 +32,7 @@ namespace osu.Desktop.Beatmaps.IO
using (var stream = new StreamReader(ReadFile(beatmaps[0]))) using (var stream = new StreamReader(ReadFile(beatmaps[0])))
{ {
var decoder = BeatmapDecoder.GetDecoder(stream); var decoder = BeatmapDecoder.GetDecoder(stream);
firstMap = new Beatmap(); firstMap = decoder.Decode(stream);
decoder.Decode(stream, firstMap);
} }
} }
@ -48,11 +48,12 @@ namespace osu.Desktop.Beatmaps.IO
public override BeatmapMetadata ReadMetadata() public override BeatmapMetadata ReadMetadata()
{ {
return firstMap.Metadata; return firstMap.BeatmapInfo.Metadata;
} }
public override void Dispose() public override void Dispose()
{ {
// no-op // no-op
} } }
}
} }

View File

@ -26,9 +26,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
var decoder = new OsuLegacyDecoder(); var decoder = new OsuLegacyDecoder();
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
{ {
Beatmap beatmap = new Beatmap(); var beatmap = decoder.Decode(new StreamReader(stream));
decoder.Decode(new StreamReader(stream), beatmap); var meta = beatmap.BeatmapInfo.Metadata;
var meta = beatmap.Metadata;
Assert.AreEqual(241526, meta.BeatmapSetID); Assert.AreEqual(241526, meta.BeatmapSetID);
Assert.AreEqual("Soleily", meta.Artist); Assert.AreEqual("Soleily", meta.Artist);
Assert.AreEqual("Soleily", meta.ArtistUnicode); Assert.AreEqual("Soleily", meta.ArtistUnicode);
@ -49,16 +48,15 @@ namespace osu.Game.Tests.Beatmaps.Formats
var decoder = new OsuLegacyDecoder(); var decoder = new OsuLegacyDecoder();
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
{ {
Beatmap beatmap = new Beatmap(); var beatmapInfo = decoder.Decode(new StreamReader(stream)).BeatmapInfo;
decoder.Decode(new StreamReader(stream), beatmap); Assert.AreEqual(0, beatmapInfo.AudioLeadIn);
Assert.AreEqual(0, beatmap.AudioLeadIn); Assert.AreEqual(false, beatmapInfo.Countdown);
Assert.AreEqual(false, beatmap.Countdown); Assert.AreEqual(SampleSet.Soft, beatmapInfo.SampleSet);
Assert.AreEqual(SampleSet.Soft, beatmap.SampleSet); Assert.AreEqual(0.7f, beatmapInfo.StackLeniency);
Assert.AreEqual(0.7f, beatmap.StackLeniency); Assert.AreEqual(false, beatmapInfo.SpecialStyle);
Assert.AreEqual(false, beatmap.SpecialStyle); Assert.AreEqual(PlayMode.Osu, beatmapInfo.Mode);
Assert.AreEqual(PlayMode.Osu, beatmap.Mode); Assert.AreEqual(false, beatmapInfo.LetterboxInBreaks);
Assert.AreEqual(false, beatmap.LetterboxInBreaks); Assert.AreEqual(false, beatmapInfo.WidescreenStoryboard);
Assert.AreEqual(false, beatmap.WidescreenStoryboard);
} }
} }
@ -68,8 +66,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
var decoder = new OsuLegacyDecoder(); var decoder = new OsuLegacyDecoder();
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
{ {
Beatmap beatmap = new Beatmap(); var beatmap = decoder.Decode(new StreamReader(stream)).BeatmapInfo;
decoder.Decode(new StreamReader(stream), beatmap);
int[] expectedBookmarks = int[] expectedBookmarks =
{ {
11505, 22054, 32604, 43153, 53703, 64252, 74802, 85351, 11505, 22054, 32604, 43153, 53703, 64252, 74802, 85351,
@ -92,9 +89,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
var decoder = new OsuLegacyDecoder(); var decoder = new OsuLegacyDecoder();
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
{ {
Beatmap beatmap = new Beatmap(); var beatmap = decoder.Decode(new StreamReader(stream));
decoder.Decode(new StreamReader(stream), beatmap); var difficulty = beatmap.BeatmapInfo.BaseDifficulty;
var difficulty = beatmap.BaseDifficulty;
Assert.AreEqual(6.5f, difficulty.DrainRate); Assert.AreEqual(6.5f, difficulty.DrainRate);
Assert.AreEqual(4, difficulty.CircleSize); Assert.AreEqual(4, difficulty.CircleSize);
Assert.AreEqual(8, difficulty.OverallDifficulty); Assert.AreEqual(8, difficulty.OverallDifficulty);
@ -110,8 +106,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
var decoder = new OsuLegacyDecoder(); var decoder = new OsuLegacyDecoder();
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
{ {
Beatmap beatmap = new Beatmap(); var beatmap = decoder.Decode(new StreamReader(stream));
decoder.Decode(new StreamReader(stream), beatmap);
Color4[] expected = Color4[] expected =
{ {
new Color4(142, 199, 255, 255), new Color4(142, 199, 255, 255),
@ -132,8 +127,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
var decoder = new OsuLegacyDecoder(); var decoder = new OsuLegacyDecoder();
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
{ {
Beatmap beatmap = new Beatmap(); var beatmap = decoder.Decode(new StreamReader(stream));
decoder.Decode(new StreamReader(stream), beatmap);
var slider = beatmap.HitObjects[0] as Slider; var slider = beatmap.HitObjects[0] as Slider;
Assert.IsNotNull(slider); Assert.IsNotNull(slider);
Assert.AreEqual(new Vector2(192, 168), slider.Position); Assert.AreEqual(new Vector2(192, 168), slider.Position);

View File

@ -1,69 +1,20 @@
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>. //Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE //Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Beatmaps.Objects; using osu.Game.Beatmaps.Objects;
using osu.Game.Beatmaps.Samples;
using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.Timing;
using osu.Game.GameModes.Play; using osu.Game.Database;
using osu.Game.Users;
using SQLite;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
{ {
public class Beatmap public class Beatmap
{ {
[PrimaryKey] public BeatmapInfo BeatmapInfo { get; set; }
public int BeatmapID { get; set; }
[NotNull, Indexed]
public int BeatmapSetID { get; set; }
[Indexed]
public int BeatmapMetadataID { get; set; }
public int BaseDifficultyID { get; set; }
[Ignore]
public List<HitObject> HitObjects { get; set; } public List<HitObject> HitObjects { get; set; }
[Ignore]
public List<ControlPoint> ControlPoints { get; set; } public List<ControlPoint> ControlPoints { get; set; }
[Ignore]
public BeatmapMetadata Metadata { get; set; }
[Ignore]
public BaseDifficulty BaseDifficulty { get; set; }
[Ignore]
public List<Color4> ComboColors { get; set; } public List<Color4> ComboColors { get; set; }
// General
public int AudioLeadIn { get; set; }
public bool Countdown { get; set; }
public SampleSet SampleSet { get; set; }
public float StackLeniency { get; set; }
public bool SpecialStyle { get; set; }
public PlayMode Mode { get; set; }
public bool LetterboxInBreaks { get; set; }
public bool WidescreenStoryboard { get; set; }
// Editor
// This bookmarks stuff is necessary because DB doesn't know how to store int[]
public string StoredBookmarks { get; internal set; }
[Ignore]
public int[] Bookmarks
{
get
{
return StoredBookmarks.Split(',').Select(b => int.Parse(b)).ToArray();
}
set
{
StoredBookmarks = string.Join(",", value);
}
}
public double DistanceSpacing { get; set; }
public int BeatDivisor { get; set; }
public int GridSize { get; set; }
public double TimelineZoom { get; set; }
// Metadata
public string Version { get; set; }
} }
} }

View File

@ -1,28 +0,0 @@
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Game.Users;
using SQLite;
namespace osu.Game.Beatmaps
{
/// <summary>
/// A beatmap set contains multiple beatmap (difficulties).
/// </summary>
public class BeatmapSet
{
[PrimaryKey]
public int BeatmapSetID { get; set; }
[NotNull, Indexed]
public int BeatmapMetadataID { get; set; }
[Ignore]
public List<Beatmap> Beatmaps { get; protected set; } = new List<Beatmap>();
[Ignore]
public BeatmapMetadata Metadata { get; set; }
[Ignore]
public User Creator { get; set; }
public string Hash { get; set; }
public string Path { get; set; }
}
}

View File

@ -15,11 +15,12 @@ namespace osu.Game.Beatmaps.Formats
throw new IOException(@"Unknown file format"); throw new IOException(@"Unknown file format");
return (BeatmapDecoder)Activator.CreateInstance(decoders[line]); return (BeatmapDecoder)Activator.CreateInstance(decoders[line]);
} }
protected static void AddDecoder<T>(string magic) where T : BeatmapDecoder protected static void AddDecoder<T>(string magic) where T : BeatmapDecoder
{ {
decoders[magic] = typeof(T); decoders[magic] = typeof(T);
} }
public abstract void Decode(TextReader stream, Beatmap beatmap); public abstract Beatmap Decode(TextReader stream);
} }
} }

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Database;
using osu.Game.Beatmaps.Events; using osu.Game.Beatmaps.Events;
using osu.Game.Beatmaps.Objects; using osu.Game.Beatmaps.Objects;
using osu.Game.Beatmaps.Samples; using osu.Game.Beatmaps.Samples;
@ -38,37 +39,38 @@ namespace osu.Game.Beatmaps.Formats
private void handleGeneral(Beatmap beatmap, string key, string val) private void handleGeneral(Beatmap beatmap, string key, string val)
{ {
var metadata = beatmap.BeatmapInfo.Metadata;
switch (key) switch (key)
{ {
case @"AudioFilename": case @"AudioFilename":
beatmap.Metadata.AudioFile = val; metadata.AudioFile = val;
break; break;
case @"AudioLeadIn": case @"AudioLeadIn":
beatmap.AudioLeadIn = int.Parse(val); beatmap.BeatmapInfo.AudioLeadIn = int.Parse(val);
break; break;
case @"PreviewTime": case @"PreviewTime":
beatmap.Metadata.PreviewTime = int.Parse(val); metadata.PreviewTime = int.Parse(val);
break; break;
case @"Countdown": case @"Countdown":
beatmap.Countdown = int.Parse(val) == 1; beatmap.BeatmapInfo.Countdown = int.Parse(val) == 1;
break; break;
case @"SampleSet": case @"SampleSet":
beatmap.SampleSet = (SampleSet)Enum.Parse(typeof(SampleSet), val); beatmap.BeatmapInfo.SampleSet = (SampleSet)Enum.Parse(typeof(SampleSet), val);
break; break;
case @"StackLeniency": case @"StackLeniency":
beatmap.StackLeniency = float.Parse(val, NumberFormatInfo.InvariantInfo); beatmap.BeatmapInfo.StackLeniency = float.Parse(val, NumberFormatInfo.InvariantInfo);
break; break;
case @"Mode": case @"Mode":
beatmap.Mode = (PlayMode)int.Parse(val); beatmap.BeatmapInfo.Mode = (PlayMode)int.Parse(val);
break; break;
case @"LetterboxInBreaks": case @"LetterboxInBreaks":
beatmap.LetterboxInBreaks = int.Parse(val) == 1; beatmap.BeatmapInfo.LetterboxInBreaks = int.Parse(val) == 1;
break; break;
case @"SpecialStyle": case @"SpecialStyle":
beatmap.SpecialStyle = int.Parse(val) == 1; beatmap.BeatmapInfo.SpecialStyle = int.Parse(val) == 1;
break; break;
case @"WidescreenStoryboard": case @"WidescreenStoryboard":
beatmap.WidescreenStoryboard = int.Parse(val) == 1; beatmap.BeatmapInfo.WidescreenStoryboard = int.Parse(val) == 1;
break; break;
} }
} }
@ -78,82 +80,84 @@ namespace osu.Game.Beatmaps.Formats
switch (key) switch (key)
{ {
case @"Bookmarks": case @"Bookmarks":
beatmap.StoredBookmarks = val; beatmap.BeatmapInfo.StoredBookmarks = val;
break; break;
case @"DistanceSpacing": case @"DistanceSpacing":
beatmap.DistanceSpacing = double.Parse(val, NumberFormatInfo.InvariantInfo); beatmap.BeatmapInfo.DistanceSpacing = double.Parse(val, NumberFormatInfo.InvariantInfo);
break; break;
case @"BeatDivisor": case @"BeatDivisor":
beatmap.BeatDivisor = int.Parse(val); beatmap.BeatmapInfo.BeatDivisor = int.Parse(val);
break; break;
case @"GridSize": case @"GridSize":
beatmap.GridSize = int.Parse(val); beatmap.BeatmapInfo.GridSize = int.Parse(val);
break; break;
case @"TimelineZoom": case @"TimelineZoom":
beatmap.TimelineZoom = double.Parse(val, NumberFormatInfo.InvariantInfo); beatmap.BeatmapInfo.TimelineZoom = double.Parse(val, NumberFormatInfo.InvariantInfo);
break; break;
} }
} }
private void handleMetadata(Beatmap beatmap, string key, string val) private void handleMetadata(Beatmap beatmap, string key, string val)
{ {
var metadata = beatmap.BeatmapInfo.Metadata;
switch (key) switch (key)
{ {
case @"Title": case @"Title":
beatmap.Metadata.Title = val; metadata.Title = val;
break; break;
case @"TitleUnicode": case @"TitleUnicode":
beatmap.Metadata.TitleUnicode = val; metadata.TitleUnicode = val;
break; break;
case @"Artist": case @"Artist":
beatmap.Metadata.Artist = val; metadata.Artist = val;
break; break;
case @"ArtistUnicode": case @"ArtistUnicode":
beatmap.Metadata.ArtistUnicode = val; metadata.ArtistUnicode = val;
break; break;
case @"Creator": case @"Creator":
beatmap.Metadata.Author = val; metadata.Author = val;
break; break;
case @"Version": case @"Version":
beatmap.Version = val; beatmap.BeatmapInfo.Version = val;
break; break;
case @"Source": case @"Source":
beatmap.Metadata.Source = val; beatmap.BeatmapInfo.Metadata.Source = val;
break; break;
case @"Tags": case @"Tags":
beatmap.Metadata.Tags = val; beatmap.BeatmapInfo.Metadata.Tags = val;
break; break;
case @"BeatmapID": case @"BeatmapID":
beatmap.BeatmapID = int.Parse(val); beatmap.BeatmapInfo.BeatmapID = int.Parse(val);
break; break;
case @"BeatmapSetID": case @"BeatmapSetID":
beatmap.BeatmapSetID = int.Parse(val); beatmap.BeatmapInfo.BeatmapSetID = int.Parse(val);
beatmap.Metadata.BeatmapSetID = int.Parse(val); metadata.BeatmapSetID = int.Parse(val);
break; break;
} }
} }
private void handleDifficulty(Beatmap beatmap, string key, string val) private void handleDifficulty(Beatmap beatmap, string key, string val)
{ {
var difficulty = beatmap.BeatmapInfo.BaseDifficulty;
switch (key) switch (key)
{ {
case @"HPDrainRate": case @"HPDrainRate":
beatmap.BaseDifficulty.DrainRate = float.Parse(val, NumberFormatInfo.InvariantInfo); difficulty.DrainRate = float.Parse(val, NumberFormatInfo.InvariantInfo);
break; break;
case @"CircleSize": case @"CircleSize":
beatmap.BaseDifficulty.CircleSize = float.Parse(val, NumberFormatInfo.InvariantInfo); difficulty.CircleSize = float.Parse(val, NumberFormatInfo.InvariantInfo);
break; break;
case @"OverallDifficulty": case @"OverallDifficulty":
beatmap.BaseDifficulty.OverallDifficulty = float.Parse(val, NumberFormatInfo.InvariantInfo); difficulty.OverallDifficulty = float.Parse(val, NumberFormatInfo.InvariantInfo);
break; break;
case @"ApproachRate": case @"ApproachRate":
beatmap.BaseDifficulty.ApproachRate = float.Parse(val, NumberFormatInfo.InvariantInfo); difficulty.ApproachRate = float.Parse(val, NumberFormatInfo.InvariantInfo);
break; break;
case @"SliderMultiplier": case @"SliderMultiplier":
beatmap.BaseDifficulty.SliderMultiplier = float.Parse(val, NumberFormatInfo.InvariantInfo); difficulty.SliderMultiplier = float.Parse(val, NumberFormatInfo.InvariantInfo);
break; break;
case @"SliderTickRate": case @"SliderTickRate":
beatmap.BaseDifficulty.SliderTickRate = float.Parse(val, NumberFormatInfo.InvariantInfo); difficulty.SliderTickRate = float.Parse(val, NumberFormatInfo.InvariantInfo);
break; break;
} }
} }
@ -176,7 +180,7 @@ namespace osu.Game.Beatmaps.Formats
type = (EventType)_type; type = (EventType)_type;
// TODO: Parse and store the rest of the event // TODO: Parse and store the rest of the event
if (type == EventType.Background) if (type == EventType.Background)
beatmap.Metadata.BackgroundFile = split[2].Trim('"'); beatmap.BeatmapInfo.Metadata.BackgroundFile = split[2].Trim('"');
} }
private void handleTimingPoints(Beatmap beatmap, string val) private void handleTimingPoints(Beatmap beatmap, string val)
@ -202,15 +206,15 @@ namespace osu.Game.Beatmaps.Formats
}); });
} }
public override void Decode(TextReader stream, Beatmap beatmap) public override Beatmap Decode(TextReader stream)
{ {
// We don't overwrite these two because they're DB bound var beatmap = new Beatmap
if (beatmap.Metadata == null) beatmap.Metadata = new BeatmapMetadata(); {
if (beatmap.BaseDifficulty == null) beatmap.BaseDifficulty = new BaseDifficulty(); HitObjects = new List<HitObject>(),
// These are fine though ControlPoints = new List<ControlPoint>(),
beatmap.HitObjects = new List<HitObject>(); ComboColors = new List<Color4>(),
beatmap.ControlPoints = new List<ControlPoint>(); BeatmapInfo = new BeatmapInfo(),
beatmap.ComboColors = new List<Color4>(); };
var section = Section.None; var section = Section.None;
string line; string line;
@ -262,10 +266,12 @@ namespace osu.Game.Beatmaps.Formats
handleColours(beatmap, key, val); handleColours(beatmap, key, val);
break; break;
case Section.HitObjects: case Section.HitObjects:
beatmap.HitObjects.Add(HitObject.Parse(beatmap.Mode, val)); beatmap.HitObjects.Add(HitObject.Parse(beatmap.BeatmapInfo.Mode, val));
break; break;
} }
} }
return beatmap;
} }
} }
} }

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Database;
namespace osu.Game.Beatmaps.IO namespace osu.Game.Beatmaps.IO
{ {

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using Ionic.Zip; using Ionic.Zip;
using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Formats;
using osu.Game.Database;
namespace osu.Game.Beatmaps.IO namespace osu.Game.Beatmaps.IO
{ {
@ -33,8 +34,7 @@ namespace osu.Game.Beatmaps.IO
using (var stream = new StreamReader(ReadFile(beatmaps[0]))) using (var stream = new StreamReader(ReadFile(beatmaps[0])))
{ {
var decoder = BeatmapDecoder.GetDecoder(stream); var decoder = BeatmapDecoder.GetDecoder(stream);
firstMap = new Beatmap(); firstMap = decoder.Decode(stream);
decoder.Decode(stream, firstMap);
} }
} }
@ -53,8 +53,9 @@ namespace osu.Game.Beatmaps.IO
public override BeatmapMetadata ReadMetadata() public override BeatmapMetadata ReadMetadata()
{ {
return firstMap.Metadata; return firstMap.BeatmapInfo.Metadata;
} }
public override void Dispose() public override void Dispose()
{ {
archive.Dispose(); archive.Dispose();

View File

@ -1,7 +1,7 @@
using System; using System;
using SQLite; using SQLite.Net.Attributes;
namespace osu.Game.Beatmaps namespace osu.Game.Database
{ {
public class BaseDifficulty public class BaseDifficulty
{ {

View File

@ -1,12 +1,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.IO; using osu.Game.Beatmaps.IO;
using SQLite; using SQLite.Net;
using SQLiteNetExtensions.Extensions;
namespace osu.Game.Database namespace osu.Game.Database
{ {
@ -23,14 +25,18 @@ namespace osu.Game.Database
connection = storage.GetDatabase(@"beatmaps"); connection = storage.GetDatabase(@"beatmaps");
connection.CreateTable<BeatmapMetadata>(); connection.CreateTable<BeatmapMetadata>();
connection.CreateTable<BaseDifficulty>(); connection.CreateTable<BaseDifficulty>();
connection.CreateTable<BeatmapSet>(); connection.CreateTable<BeatmapSetInfo>();
connection.CreateTable<Beatmap>(); connection.CreateTable<BeatmapInfo>();
} }
} }
public void AddBeatmap(string path)
public void ImportBeatmap(string path)
{ {
string hash = null; string hash = null;
ArchiveReader reader; var reader = ArchiveReader.GetReader(storage, path);
var metadata = reader.ReadMetadata();
if (connection.Table<BeatmapSetInfo>().Count(b => b.BeatmapSetID == metadata.BeatmapSetID) != 0)
return; // TODO: Update this beatmap instead
if (File.Exists(path)) // Not always the case, i.e. for LegacyFilesystemReader if (File.Exists(path)) // Not always the case, i.e. for LegacyFilesystemReader
{ {
using (var md5 = MD5.Create()) using (var md5 = MD5.Create())
@ -41,60 +47,82 @@ namespace osu.Game.Database
var outputPath = Path.Combine(@"beatmaps", hash.Remove(1), hash.Remove(2), hash); var outputPath = Path.Combine(@"beatmaps", hash.Remove(1), hash.Remove(2), hash);
using (var output = storage.GetStream(outputPath, FileAccess.Write)) using (var output = storage.GetStream(outputPath, FileAccess.Write))
input.CopyTo(output); input.CopyTo(output);
reader = ArchiveReader.GetReader(storage, path = outputPath);
} }
} }
else
reader = ArchiveReader.GetReader(storage, path);
var metadata = reader.ReadMetadata();
if (connection.Table<BeatmapSet>().Count(b => b.BeatmapSetID == metadata.BeatmapSetID) != 0)
return; // TODO: Update this beatmap instead
string[] mapNames = reader.ReadBeatmaps(); string[] mapNames = reader.ReadBeatmaps();
var beatmapSet = new BeatmapSet var beatmapSet = new BeatmapSetInfo
{ {
BeatmapSetID = metadata.BeatmapSetID, BeatmapSetID = metadata.BeatmapSetID,
Path = path, Path = path,
Hash = hash, Hash = hash,
}; };
var maps = new List<Beatmap>(); var maps = new List<BeatmapInfo>();
foreach (var name in mapNames) foreach (var name in mapNames)
{ {
using (var stream = new StreamReader(reader.ReadFile(name))) using (var stream = new StreamReader(reader.ReadFile(name)))
{ {
var decoder = BeatmapDecoder.GetDecoder(stream); var decoder = BeatmapDecoder.GetDecoder(stream);
Beatmap beatmap = new Beatmap(); Beatmap beatmap = decoder.Decode(stream);
decoder.Decode(stream, beatmap); beatmap.BeatmapInfo.Path = name;
maps.Add(beatmap); // TODO: Diff beatmap metadata with set metadata and insert if necessary
beatmap.BaseDifficultyID = connection.Insert(beatmap.BaseDifficulty); beatmap.BeatmapInfo.Metadata = null;
maps.Add(beatmap.BeatmapInfo);
connection.Insert(beatmap.BeatmapInfo.BaseDifficulty);
connection.Insert(beatmap.BeatmapInfo);
connection.UpdateWithChildren(beatmap.BeatmapInfo);
} }
} }
beatmapSet.BeatmapMetadataID = connection.Insert(metadata);
connection.Insert(beatmapSet); connection.Insert(beatmapSet);
connection.InsertAll(maps); beatmapSet.BeatmapMetadataID = connection.Insert(metadata);
connection.UpdateWithChildren(beatmapSet);
} }
public ArchiveReader GetReader(BeatmapSet beatmapSet)
public ArchiveReader GetReader(BeatmapSetInfo beatmapSet)
{ {
return ArchiveReader.GetReader(storage, beatmapSet.Path); return ArchiveReader.GetReader(storage, beatmapSet.Path);
} }
/// <summary> public BeatmapSetInfo GetBeatmapSet(int id)
/// Given a BeatmapSet pulled from the database, loads the rest of its data from disk.
/// </summary> public void PopulateBeatmap(BeatmapSet beatmapSet)
{ {
return Query<BeatmapSetInfo>().Where(s => s.BeatmapSetID == id).FirstOrDefault();
}
public Beatmap GetBeatmap(BeatmapInfo beatmapInfo)
{
var beatmapSet = Query<BeatmapSetInfo>()
.Where(s => s.BeatmapSetID == beatmapInfo.BeatmapSetID).FirstOrDefault();
if (beatmapSet == null)
throw new InvalidOperationException(
$@"Beatmap set {beatmapInfo.BeatmapSetID} is not in the local database.");
using (var reader = GetReader(beatmapSet)) using (var reader = GetReader(beatmapSet))
{ using (var stream = new StreamReader(reader.ReadFile(beatmapInfo.Path)))
string[] mapNames = reader.ReadBeatmaps();
foreach (var name in mapNames)
{
using (var stream = new StreamReader(reader.ReadFile(name)))
{ {
var decoder = BeatmapDecoder.GetDecoder(stream); var decoder = BeatmapDecoder.GetDecoder(stream);
Beatmap beatmap = new Beatmap(); return decoder.Decode(stream);
decoder.Decode(stream, beatmap); }
beatmapSet.Beatmaps.Add(beatmap); }
}
} public TableQuery<T> Query<T>() where T : class
} {
return connection.Table<T>();
}
readonly Type[] validTypes = new[]
{
typeof(BeatmapSetInfo),
typeof(BeatmapInfo),
typeof(BeatmapMetadata),
typeof(BaseDifficulty),
};
public void Update<T>(T record, bool cascade = true) where T : class
{
if (!validTypes.Any(t => t == typeof(T)))
throw new ArgumentException(nameof(T), "Must be a type managed by BeatmapDatabase");
if (cascade)
connection.UpdateWithChildren(record);
else
connection.Update(record);
} }
} }
} }

View File

@ -0,0 +1,65 @@
using System;
using System.Linq;
using osu.Game.Beatmaps.Samples;
using osu.Game.GameModes.Play;
using SQLite.Net.Attributes;
using SQLiteNetExtensions.Attributes;
namespace osu.Game.Database
{
public class BeatmapInfo
{
public BeatmapInfo()
{
BaseDifficulty = new BaseDifficulty();
}
[PrimaryKey]
public int BeatmapID { get; set; }
[NotNull, Indexed]
public int BeatmapSetID { get; set; }
[ForeignKey(typeof(BeatmapMetadata))]
public int BeatmapMetadataID { get; set; }
[ForeignKey(typeof(BaseDifficulty)), NotNull]
public int BaseDifficultyID { get; set; }
[OneToOne]
public BeatmapMetadata Metadata { get; set; }
[OneToOne]
public BaseDifficulty BaseDifficulty { get; set; }
public string Path { get; set; }
// General
public int AudioLeadIn { get; set; }
public bool Countdown { get; set; }
public SampleSet SampleSet { get; set; }
public float StackLeniency { get; set; }
public bool SpecialStyle { get; set; }
public PlayMode Mode { get; set; }
public bool LetterboxInBreaks { get; set; }
public bool WidescreenStoryboard { get; set; }
// Editor
// This bookmarks stuff is necessary because DB doesn't know how to store int[]
public string StoredBookmarks { get; internal set; }
[Ignore]
public int[] Bookmarks
{
get
{
return StoredBookmarks.Split(',').Select(b => int.Parse(b)).ToArray();
}
set
{
StoredBookmarks = string.Join(",", value);
}
}
public double DistanceSpacing { get; set; }
public int BeatDivisor { get; set; }
public int GridSize { get; set; }
public double TimelineZoom { get; set; }
// Metadata
public string Version { get; set; }
}
}

View File

@ -2,9 +2,9 @@
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE //Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.GameModes.Play; using osu.Game.GameModes.Play;
using SQLite; using SQLite.Net.Attributes;
namespace osu.Game.Beatmaps namespace osu.Game.Database
{ {
public class BeatmapMetadata public class BeatmapMetadata
{ {

View File

@ -0,0 +1,19 @@
using System;
using SQLite.Net.Attributes;
using SQLiteNetExtensions.Attributes;
namespace osu.Game.Database
{
public class BeatmapSetInfo
{
[PrimaryKey]
public int BeatmapSetID { get; set; }
[OneToOne]
public BeatmapMetadata Metadata { get; set; }
[NotNull, ForeignKey(typeof(BeatmapMetadata))]
public int BeatmapMetadataID { get; set; }
public string Hash { get; set; }
public string Path { get; set; }
}
}

View File

@ -74,7 +74,7 @@ namespace osu.Game
{ {
try try
{ {
Beatmaps.AddBeatmap(message.Path); Beatmaps.ImportBeatmap(message.Path);
// TODO: Switch to beatmap list and select the new song // TODO: Switch to beatmap list and select the new song
} }
catch (Exception ex) catch (Exception ex)

View File

@ -43,26 +43,24 @@
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="SQLitePCLRaw.core">
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.core.1.0.1\lib\net45\SQLitePCLRaw.core.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.provider.e_sqlite3">
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.0.1\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.batteries_green">
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.0.1\lib\net45\SQLitePCLRaw.batteries_green.dll</HintPath>
</Reference>
<Reference Include="SQLite-net">
<HintPath>$(SolutionDir)\packages\sqlite-net-pcl.1.2.0\lib\portable-net45+wp8+wpa81+win8+MonoAndroid10+MonoTouch10+Xamarin.iOS10\SQLite-net.dll</HintPath>
</Reference>
<Reference Include="DotNetZip"> <Reference Include="DotNetZip">
<HintPath>$(SolutionDir)\packages\DotNetZip.1.10.1\lib\net20\DotNetZip.dll</HintPath> <HintPath>$(SolutionDir)\packages\DotNetZip.1.10.1\lib\net20\DotNetZip.dll</HintPath>
</Reference> </Reference>
<Reference Include="SQLite.Net.Platform.Win32">
<HintPath>$(SolutionDir)\packages\SQLite.Net-PCL.3.0.5\lib\net4\SQLite.Net.Platform.Win32.dll</HintPath>
</Reference>
<Reference Include="SQLite.Net">
<HintPath>$(SolutionDir)\packages\SQLite.Net-PCL.3.0.5\lib\net40\SQLite.Net.dll</HintPath>
</Reference>
<Reference Include="SQLite.Net.Platform.Generic">
<HintPath>$(SolutionDir)\packages\SQLite.Net-PCL.3.0.5\lib\net40\SQLite.Net.Platform.Generic.dll</HintPath>
</Reference>
<Reference Include="SQLiteNetExtensions">
<HintPath>$(SolutionDir)\packages\SQLiteNetExtensions.1.3.0\lib\portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1\SQLiteNetExtensions.dll</HintPath>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Beatmaps\Beatmap.cs" /> <Compile Include="Beatmaps\Beatmap.cs" />
<Compile Include="Beatmaps\BeatmapSet.cs" />
<Compile Include="Beatmaps\BeatmapMetadata.cs" />
<Compile Include="Beatmaps\Objects\Catch\CatchConverter.cs" /> <Compile Include="Beatmaps\Objects\Catch\CatchConverter.cs" />
<Compile Include="Beatmaps\Objects\Catch\Drawable\DrawableFruit.cs" /> <Compile Include="Beatmaps\Objects\Catch\Drawable\DrawableFruit.cs" />
<Compile Include="Beatmaps\Objects\HitObject.cs" /> <Compile Include="Beatmaps\Objects\HitObject.cs" />
@ -168,9 +166,12 @@
<Compile Include="Beatmaps\Formats\BeatmapDecoder.cs" /> <Compile Include="Beatmaps\Formats\BeatmapDecoder.cs" />
<Compile Include="Beatmaps\Formats\OsuLegacyDecoder.cs" /> <Compile Include="Beatmaps\Formats\OsuLegacyDecoder.cs" />
<Compile Include="Beatmaps\IO\OszArchiveReader.cs" /> <Compile Include="Beatmaps\IO\OszArchiveReader.cs" />
<Compile Include="Beatmaps\BaseDifficulty.cs" />
<Compile Include="Beatmaps\Events\EventType.cs" /> <Compile Include="Beatmaps\Events\EventType.cs" />
<Compile Include="VolumeMeter.cs" /> <Compile Include="VolumeMeter.cs" />
<Compile Include="Database\BeatmapSetInfo.cs" />
<Compile Include="Database\BeatmapMetadata.cs" />
<Compile Include="Database\BeatmapInfo.cs" />
<Compile Include="Database\BaseDifficulty.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="$(SolutionDir)\osu-framework\osu.Framework\osu.Framework.csproj"> <ProjectReference Include="$(SolutionDir)\osu-framework\osu.Framework\osu.Framework.csproj">
@ -198,7 +199,4 @@
<Target Name="AfterBuild"> <Target Name="AfterBuild">
</Target> </Target>
--> -->
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.0.1\build\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets" Condition="Exists('..\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.0.1\build\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" />
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.0.1\build\SQLitePCLRaw.lib.e_sqlite3.osx.targets" Condition="Exists('..\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.0.1\build\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" />
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.0.1\build\SQLitePCLRaw.lib.e_sqlite3.linux.targets" Condition="Exists('..\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.0.1\build\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" />
</Project> </Project>

View File

@ -7,11 +7,6 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
<package id="DotNetZip" version="1.10.1" targetFramework="net45" /> <package id="DotNetZip" version="1.10.1" targetFramework="net45" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" /> <package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
<package id="ppy.OpenTK" version="1.1.2225.3" targetFramework="net45" /> <package id="ppy.OpenTK" version="1.1.2225.3" targetFramework="net45" />
<package id="sqlite-net-pcl" version="1.2.0" targetFramework="net45" /> <package id="SQLite.Net-PCL" version="3.0.5" targetFramework="net45" />
<package id="SQLitePCLRaw.bundle_green" version="1.0.1" targetFramework="net45" /> <package id="SQLiteNetExtensions" version="1.3.0" targetFramework="net45" />
<package id="SQLitePCLRaw.core" version="1.0.1" targetFramework="net45" />
<package id="SQLitePCLRaw.lib.e_sqlite3.linux" version="1.0.1" targetFramework="net45" />
<package id="SQLitePCLRaw.lib.e_sqlite3.osx" version="1.0.1" targetFramework="net45" />
<package id="SQLitePCLRaw.lib.e_sqlite3.v110_xp" version="1.0.1" targetFramework="net45" />
<package id="SQLitePCLRaw.provider.e_sqlite3.net45" version="1.0.1" targetFramework="net45" />
</packages> </packages>