2016-10-14 11:33:58 +08:00
|
|
|
|
using System;
|
2016-10-20 01:13:14 +08:00
|
|
|
|
using System.Collections;
|
2016-10-05 04:29:08 +08:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
2016-10-19 03:38:59 +08:00
|
|
|
|
using System.Linq;
|
2016-10-20 00:51:18 +08:00
|
|
|
|
using System.Linq.Expressions;
|
2016-10-13 22:15:08 +08:00
|
|
|
|
using System.Security.Cryptography;
|
2016-10-10 21:20:06 +08:00
|
|
|
|
using osu.Framework.Platform;
|
2016-10-08 01:50:34 +08:00
|
|
|
|
using osu.Game.Beatmaps;
|
2016-10-05 04:29:08 +08:00
|
|
|
|
using osu.Game.Beatmaps.Formats;
|
|
|
|
|
using osu.Game.Beatmaps.IO;
|
2016-10-19 01:35:01 +08:00
|
|
|
|
using SQLite.Net;
|
|
|
|
|
using SQLiteNetExtensions.Extensions;
|
2016-10-04 23:31:10 +08:00
|
|
|
|
|
2016-10-14 11:33:58 +08:00
|
|
|
|
namespace osu.Game.Database
|
|
|
|
|
{
|
|
|
|
|
public class BeatmapDatabase
|
|
|
|
|
{
|
|
|
|
|
private static SQLiteConnection connection { get; set; }
|
|
|
|
|
private BasicStorage storage;
|
2016-10-12 01:52:16 +08:00
|
|
|
|
public event Action<BeatmapSetInfo> BeatmapSetAdded;
|
2016-10-14 11:33:58 +08:00
|
|
|
|
|
|
|
|
|
public BeatmapDatabase(BasicStorage storage)
|
|
|
|
|
{
|
|
|
|
|
this.storage = storage;
|
|
|
|
|
if (connection == null)
|
|
|
|
|
{
|
|
|
|
|
connection = storage.GetDatabase(@"beatmaps");
|
|
|
|
|
connection.CreateTable<BeatmapMetadata>();
|
|
|
|
|
connection.CreateTable<BaseDifficulty>();
|
2016-10-19 01:35:01 +08:00
|
|
|
|
connection.CreateTable<BeatmapSetInfo>();
|
|
|
|
|
connection.CreateTable<BeatmapInfo>();
|
2016-10-14 11:33:58 +08:00
|
|
|
|
}
|
2016-10-05 04:29:08 +08:00
|
|
|
|
}
|
2016-10-19 01:35:01 +08:00
|
|
|
|
|
2016-10-21 16:01:46 +08:00
|
|
|
|
public void Reset()
|
|
|
|
|
{
|
|
|
|
|
foreach (var setInfo in Query<BeatmapSetInfo>())
|
|
|
|
|
storage.Delete(setInfo.Path);
|
|
|
|
|
|
|
|
|
|
connection.DeleteAll<BeatmapMetadata>();
|
|
|
|
|
connection.DeleteAll<BaseDifficulty>();
|
|
|
|
|
connection.DeleteAll<BeatmapSetInfo>();
|
|
|
|
|
connection.DeleteAll<BeatmapInfo>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Import(string path)
|
2016-10-14 11:33:58 +08:00
|
|
|
|
{
|
|
|
|
|
string hash = null;
|
2016-10-19 23:00:11 +08:00
|
|
|
|
BeatmapMetadata metadata;
|
2016-10-21 16:01:34 +08:00
|
|
|
|
|
2016-10-19 23:00:11 +08:00
|
|
|
|
using (var reader = ArchiveReader.GetReader(storage, path))
|
|
|
|
|
metadata = reader.ReadMetadata();
|
2016-10-21 16:01:34 +08:00
|
|
|
|
|
2016-10-19 03:41:46 +08:00
|
|
|
|
if (connection.Table<BeatmapSetInfo>().Count(b => b.BeatmapSetID == metadata.BeatmapSetID) != 0)
|
|
|
|
|
return; // TODO: Update this beatmap instead
|
2016-10-21 16:01:34 +08:00
|
|
|
|
|
2016-10-14 11:33:58 +08:00
|
|
|
|
if (File.Exists(path)) // Not always the case, i.e. for LegacyFilesystemReader
|
|
|
|
|
{
|
|
|
|
|
using (var md5 = MD5.Create())
|
|
|
|
|
using (var input = storage.GetStream(path))
|
|
|
|
|
{
|
|
|
|
|
hash = BitConverter.ToString(md5.ComputeHash(input)).Replace("-", "").ToLowerInvariant();
|
|
|
|
|
input.Seek(0, SeekOrigin.Begin);
|
2016-10-21 16:01:34 +08:00
|
|
|
|
path = Path.Combine(@"beatmaps", hash.Remove(1), hash.Remove(2), hash);
|
|
|
|
|
using (var output = storage.GetStream(path, FileAccess.Write))
|
2016-10-14 11:33:58 +08:00
|
|
|
|
input.CopyTo(output);
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-10-19 01:35:01 +08:00
|
|
|
|
var beatmapSet = new BeatmapSetInfo
|
2016-10-14 11:33:58 +08:00
|
|
|
|
{
|
|
|
|
|
BeatmapSetID = metadata.BeatmapSetID,
|
2016-10-20 02:02:03 +08:00
|
|
|
|
Beatmaps = new List<BeatmapInfo>(),
|
2016-10-14 11:33:58 +08:00
|
|
|
|
Path = path,
|
|
|
|
|
Hash = hash,
|
2016-10-21 16:01:34 +08:00
|
|
|
|
Metadata = metadata
|
2016-10-14 11:33:58 +08:00
|
|
|
|
};
|
2016-10-21 16:01:34 +08:00
|
|
|
|
|
2016-10-19 23:00:11 +08:00
|
|
|
|
using (var reader = ArchiveReader.GetReader(storage, path))
|
2016-10-14 11:33:58 +08:00
|
|
|
|
{
|
2016-10-19 23:00:11 +08:00
|
|
|
|
string[] mapNames = reader.ReadBeatmaps();
|
|
|
|
|
foreach (var name in mapNames)
|
2016-10-05 04:29:08 +08:00
|
|
|
|
{
|
2016-10-19 23:00:11 +08:00
|
|
|
|
using (var stream = new StreamReader(reader.ReadFile(name)))
|
|
|
|
|
{
|
|
|
|
|
var decoder = BeatmapDecoder.GetDecoder(stream);
|
|
|
|
|
Beatmap beatmap = decoder.Decode(stream);
|
|
|
|
|
beatmap.BeatmapInfo.Path = name;
|
2016-10-21 16:01:34 +08:00
|
|
|
|
|
2016-10-19 23:00:11 +08:00
|
|
|
|
// TODO: Diff beatmap metadata with set metadata and leave it here if necessary
|
|
|
|
|
beatmap.BeatmapInfo.Metadata = null;
|
2016-10-21 16:01:34 +08:00
|
|
|
|
|
2016-10-20 02:02:03 +08:00
|
|
|
|
beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo);
|
2016-10-19 23:00:11 +08:00
|
|
|
|
}
|
2016-10-14 11:33:58 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-10-21 16:01:34 +08:00
|
|
|
|
connection.InsertWithChildren(beatmapSet, true);
|
2016-10-12 01:52:16 +08:00
|
|
|
|
BeatmapSetAdded?.Invoke(beatmapSet);
|
2016-10-13 22:29:30 +08:00
|
|
|
|
}
|
2016-10-14 11:33:58 +08:00
|
|
|
|
|
2016-10-19 01:35:01 +08:00
|
|
|
|
public ArchiveReader GetReader(BeatmapSetInfo beatmapSet)
|
2016-10-14 11:33:58 +08:00
|
|
|
|
{
|
2016-10-19 01:35:01 +08:00
|
|
|
|
return ArchiveReader.GetReader(storage, beatmapSet.Path);
|
2016-10-14 11:33:58 +08:00
|
|
|
|
}
|
2016-10-19 03:38:59 +08:00
|
|
|
|
|
2016-10-19 03:48:24 +08:00
|
|
|
|
public BeatmapSetInfo GetBeatmapSet(int id)
|
|
|
|
|
{
|
|
|
|
|
return Query<BeatmapSetInfo>().Where(s => s.BeatmapSetID == id).FirstOrDefault();
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-19 03:38:59 +08:00
|
|
|
|
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 stream = new StreamReader(reader.ReadFile(beatmapInfo.Path)))
|
|
|
|
|
{
|
|
|
|
|
var decoder = BeatmapDecoder.GetDecoder(stream);
|
|
|
|
|
return decoder.Decode(stream);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public TableQuery<T> Query<T>() where T : class
|
|
|
|
|
{
|
|
|
|
|
return connection.Table<T>();
|
|
|
|
|
}
|
2016-10-20 00:51:18 +08:00
|
|
|
|
|
|
|
|
|
public T GetWithChildren<T>(object id) where T : class
|
|
|
|
|
{
|
|
|
|
|
return connection.GetWithChildren<T>(id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public List<T> GetAllWithChildren<T>(Expression<Func<T, bool>> filter = null,
|
|
|
|
|
bool recursive = true) where T : class
|
|
|
|
|
{
|
|
|
|
|
return connection.GetAllWithChildren<T>(filter, recursive);
|
|
|
|
|
}
|
2016-10-21 03:42:09 +08:00
|
|
|
|
|
2016-10-21 16:02:00 +08:00
|
|
|
|
public T GetChildren<T>(T item, bool recursive = true)
|
2016-10-21 03:42:09 +08:00
|
|
|
|
{
|
2016-10-21 16:02:00 +08:00
|
|
|
|
if (item == null) return default(T);
|
|
|
|
|
|
|
|
|
|
connection.GetChildren(item, recursive);
|
|
|
|
|
return item;
|
2016-10-21 03:42:09 +08:00
|
|
|
|
}
|
2016-10-19 03:38:59 +08:00
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
2016-10-14 11:33:58 +08:00
|
|
|
|
}
|
2016-10-19 23:00:11 +08:00
|
|
|
|
}
|