private void LoadPlaylist() { OsuDatabase db = new OsuDatabase(PathHelper.Combine("osu!.db")); var result = new List <PlaylistItem>(); int id = 0; foreach (var i in db.Beatmaps) { bool shouldBeAdded = result.Any(x => x.Directory == i.FolderName && x.Audiofile == i.AudioFileName); if (!shouldBeAdded) { result.Add(new PlaylistItem { Index = id++, Audiofile = i.AudioFileName, Artist = i.ArtistName, Title = i.SongTitle, Directory = i.FolderName, OsuFile = i.NameOfOsuFile, IsCurrent = false }); } } PlaylistItems = new ObservableCollection <PlaylistItem>(result); }
private void loadOsuDb() { osuRoot = File.ReadAllText(@"osudir.txt"); osuDb = DatabaseDecoder.DecodeOsu(Path.Combine(osuRoot, @"osu!.db")); mapsByHash = new Dictionary <string, DbBeatmap>(); foreach (var map in osuDb.Beatmaps) { mapsByHash.Add(map.MD5Hash, map); } }
private async ValueTask MaintanceDatabase(string dbPath, CancellationToken token) { using var reader = new OsuDatabaseReader(dbPath); var newInfo = reader.ReadOsuDatabaseHead(); // Do update or create operation depend on dbCount var dbCount = await Db.OsuDatabases.CountAsync(); OsuDatabase currentInfo = null; if (dbCount > 0) { currentInfo = await Db.OsuDatabases.FirstAsync(); } if (dbCount > 0 && currentInfo != null && currentInfo.BeatmapCount == newInfo.BeatmapCount) { Logger.LogInformation($"Database up-to-date."); return; } Logger.LogInformation("Beatmap count mismatch, start synchronize..."); reader.ReadOsuDatabase(ref newInfo); var beatmaps = newInfo.Beatmaps; newInfo.Beatmaps = null; await Db.Database.AutoCommitTransactionScope(async() => { // dbCount == 0, do create operation if (dbCount == 0) { await CreateDatabase(newInfo, beatmaps, token); } // else do update operation else { await UpdateDatabase(currentInfo, newInfo, beatmaps, token); } Logger.LogInformation($"Synchronize complete, {newInfo.BeatmapCount} saved..."); // if token is cancelled, throw will let db operation rollback token.ThrowIfCancellationRequested(); return(true); }); }
/// <summary> /// Create a new database by loaded osu!db content /// </summary> /// <param name="dbInfo"></param> /// <param name="beatmaps"></param> /// <param name="token"></param> /// <returns></returns> private async ValueTask CreateDatabase(OsuDatabase dbInfo, List <OsuDatabaseBeatmap> beatmaps, CancellationToken token = default) { Logger.LogInformation("Creating database..."); await Db.AddAsync(dbInfo); await Db.SaveChangesAsync(); Logger.LogInformation($"Processing {dbInfo.BeatmapCount} beatmap..."); DateTimeOffset startTime = DateTimeOffset.Now; Db.RemoveRange(Db.OsuDatabaseBeatmap); await Db.SaveChangesAsync(); // Save all beatmaps foreach (var beatmap in beatmaps) { if (token.IsCancellationRequested) { return; } beatmap.OsuDatabaseId = dbInfo.Id; beatmap.OsuDatabaseTimings = null; beatmap.StarRatings = null; } // for performance, we save change every 5000 beatmaps foreach (var chunk in beatmaps.Chunk(5000)) { if (token.IsCancellationRequested) { return; } await Db.AddRangeAsync(chunk); await Db.SaveChangesAsync(); Logger.LogInformation($"Processed {chunk.Count} beatmap."); } Logger.LogInformation($"Create database complete in {(DateTimeOffset.Now - startTime).TotalMilliseconds}ms"); }
/// <summary> /// Parses osu!.db file. /// </summary> /// <param name="stream">Stream containing osu!.db data.</param> /// <returns>A usable <see cref="OsuDatabase"/>.</returns> public static OsuDatabase DecodeOsu(Stream stream) { OsuDatabase db = new OsuDatabase(); using (var r = new SerializationReader(stream)) { db.OsuVersion = r.ReadInt32(); db.FolderCount = r.ReadInt32(); db.AccountUnlocked = r.ReadBoolean(); db.UnlockDate = r.ReadDateTime(); db.PlayerName = r.ReadString(); int beatmapCount = r.ReadInt32(); db.BeatmapCount = beatmapCount; for (int i = 0; i < beatmapCount; i++) { DbBeatmap beatmap = new DbBeatmap(); if (db.OsuVersion < 20191106) { beatmap.BytesOfBeatmapEntry = r.ReadInt32(); } beatmap.Artist = r.ReadString(); beatmap.ArtistUnicode = r.ReadString(); beatmap.Title = r.ReadString(); beatmap.TitleUnicode = r.ReadString(); beatmap.Creator = r.ReadString(); beatmap.Difficulty = r.ReadString(); beatmap.AudioFileName = r.ReadString(); beatmap.MD5Hash = r.ReadString(); beatmap.FileName = r.ReadString(); beatmap.RankedStatus = (RankedStatus)r.ReadByte(); beatmap.CirclesCount = r.ReadUInt16(); beatmap.SlidersCount = r.ReadUInt16(); beatmap.SpinnersCount = r.ReadUInt16(); beatmap.LastModifiedTime = r.ReadDateTime(); beatmap.ApproachRate = db.OsuVersion >= 20140609 ? r.ReadSingle() : r.ReadByte(); beatmap.CircleSize = db.OsuVersion >= 20140609 ? r.ReadSingle() : r.ReadByte(); beatmap.HPDrain = db.OsuVersion >= 20140609 ? r.ReadSingle() : r.ReadByte(); beatmap.OverallDifficulty = db.OsuVersion >= 20140609 ? r.ReadSingle() : r.ReadByte(); beatmap.SliderVelocity = r.ReadDouble(); if (db.OsuVersion >= 20140609) { beatmap.StandardStarRating = r.ReadDictionary <Mods, double>(); beatmap.TaikoStarRating = r.ReadDictionary <Mods, double>(); beatmap.CatchStarRating = r.ReadDictionary <Mods, double>(); beatmap.ManiaStarRating = r.ReadDictionary <Mods, double>(); } beatmap.DrainTime = r.ReadInt32(); beatmap.TotalTime = r.ReadInt32(); beatmap.AudioPreviewTime = r.ReadInt32(); int timingPointsCount = r.ReadInt32(); for (int j = 0; j < timingPointsCount; j++) { DbTimingPoint timingPoint = new DbTimingPoint(); timingPoint.BPM = r.ReadDouble(); timingPoint.Offset = r.ReadDouble(); timingPoint.Inherited = r.ReadBoolean(); beatmap.TimingPoints.Add(timingPoint); } beatmap.BeatmapId = r.ReadInt32(); beatmap.BeatmapSetId = r.ReadInt32(); beatmap.ThreadId = r.ReadInt32(); beatmap.StandardGrade = (Grade)r.ReadByte(); beatmap.TaikoGrade = (Grade)r.ReadByte(); beatmap.CatchGrade = (Grade)r.ReadByte(); beatmap.ManiaGrade = (Grade)r.ReadByte(); beatmap.LocalOffset = r.ReadInt16(); beatmap.StackLeniency = r.ReadSingle(); beatmap.Ruleset = (Ruleset)r.ReadByte(); beatmap.Source = r.ReadString(); beatmap.Tags = r.ReadString(); beatmap.OnlineOffset = r.ReadInt16(); beatmap.TitleFont = r.ReadString(); beatmap.IsUnplayed = r.ReadBoolean(); beatmap.LastPlayed = r.ReadDateTime(); beatmap.IsOsz2 = r.ReadBoolean(); beatmap.FolderName = r.ReadString(); beatmap.LastCheckedAgainstOsuRepo = r.ReadDateTime(); beatmap.IgnoreBeatmapSound = r.ReadBoolean(); beatmap.IgnoreBeatmapSkin = r.ReadBoolean(); beatmap.DisableStoryboard = r.ReadBoolean(); beatmap.DisableVideo = r.ReadBoolean(); beatmap.VisualOverride = r.ReadBoolean(); if (db.OsuVersion < 20140609) { r.BaseStream.Seek(sizeof(short), SeekOrigin.Current); //let's skip this unknown variable } r.BaseStream.Seek(sizeof(int), SeekOrigin.Current); //and this one beatmap.ManiaScrollSpeed = r.ReadByte(); db.Beatmaps.Add(beatmap); } db.Permissions = (Permissions)r.ReadInt32(); } return(db); }
/// <summary> /// /// </summary> /// <param name="beatmapBase">Object that</param> public OsuFileIo(Beatmap beatmapBase) { OsuDatabase = new OsuDatabase(beatmapBase); CollectionLoader = new CollectionLoader(OsuDatabase.LoadedMaps); ScoresLoader = new ScoresDatabaseIo(ScoresDatabase); }
/// <summary> /// The main entry point for the application. /// </summary> // [STAThread] static void Main() { var osuPath = @"C:\Users\sunny\Programs\osu!\"; var songsPath = Path.Join(osuPath, "Songs"); var simPath = @"C:\Users\sunny\Programs\StepMania 5.1\Songs\Test"; var osuDbPath = Path.Join(osuPath, "osu!.db"); OsuDatabase db = DatabaseDecoder.DecodeOsu(osuDbPath); var songs = Directory.GetDirectories(songsPath); Dictionary <int, List <DbBeatmap> > bms = db.Beatmaps .GroupBy(x => x.BeatmapSetId, x => x) .ToDictionary(x => x.Key, x => x.ToList()); foreach (var songPath in songs) { var songFolder = Path.GetFileName(songPath); Console.WriteLine(songFolder); var maps = Directory.GetFiles(Path.Join(songsPath, songFolder), "*.osu"); Simfile s = null; List <DbBeatmap> bs = null; foreach (var mapPath in maps) { var songName = Path.GetFileNameWithoutExtension(mapPath); Console.WriteLine(" " + songName); Beatmap beatmap = BeatmapDecoder.Decode(Path.Join(songsPath, songFolder, songName + ".osu")); if (bs == null) { bs = bms[beatmap.MetadataSection.BeatmapSetID]; bs.Sort((x, y) => x.CirclesCount .CompareTo(y.CirclesCount)); } BasicSongInfo songInfo = OsuToAlgorithm.Convert(beatmap); Random rng = new Random(beatmap.MetadataSection.BeatmapID); StepScoreGenerator sg = new StepScoreGenerator { Rng = rng, }; Algorithm a = new Algorithm { Info = songInfo, RecoveryInterval = songInfo.PPQ / 2 / 8, StepScore = sg.RandomStepScore(songInfo), }; if (s == null) { s = AlgorithmToSimfile.Convert(beatmap, songInfo, new List <Simfile.Chart>()); } var x = a.Run(); var chart = AlgorithmToSimfile.ConvertChart(beatmap, songInfo, x, diff: bs.FindIndex(y => y.BeatmapId == beatmap.MetadataSection.BeatmapID) ); s.Charts.Add(chart); } Directory.CreateDirectory(Path.Join(simPath, songFolder)); var sb = new StringBuilder(); s.Append(sb); File.WriteAllText(Path.Join(simPath, songFolder, songFolder + ".sm"), sb.ToString()); File.Copy(Path.Join(songsPath, songFolder, s.Music), Path.Join(simPath, songFolder, s.Music), true); } //Application.SetHighDpiMode(HighDpiMode.SystemAware); //Application.EnableVisualStyles(); //Application.SetCompatibleTextRenderingDefault(false); //Application.Run(new Form1()); }
public static void EncodeOsuDatabase(string path, OsuDatabase db) { using (SerializationWriter writer = new SerializationWriter(new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))) { writer.Write(db.OsuVersion); writer.Write(db.FolderCount); writer.Write(db.AccountUnlocked); writer.Write(db.UnlockDate); writer.Write(db.PlayerName); writer.Write(db.BeatmapCount); foreach (var beatmap in db.Beatmaps) { if (db.OsuVersion < 20191106) { writer.Write(beatmap.BytesOfBeatmapEntry); } writer.Write(beatmap.Artist); writer.Write(beatmap.ArtistUnicode); writer.Write(beatmap.Title); writer.Write(beatmap.TitleUnicode); writer.Write(beatmap.Creator); writer.Write(beatmap.Difficulty); writer.Write(beatmap.AudioFileName); writer.Write(beatmap.MD5Hash); writer.Write(beatmap.FileName); writer.Write((byte)beatmap.RankedStatus); writer.Write(beatmap.CirclesCount); writer.Write(beatmap.SlidersCount); writer.Write(beatmap.SpinnersCount); writer.Write(beatmap.LastModifiedTime); writer.Write(beatmap.ApproachRate); writer.Write(beatmap.CircleSize); writer.Write(beatmap.HPDrain); writer.Write(beatmap.OverallDifficulty); writer.Write(beatmap.SliderVelocity); if (db.OsuVersion >= 20140609) { writer.Write(beatmap.StandardStarRating.ToDictionary(d => (int)d.Key, d => d.Value)); writer.Write(beatmap.TaikoStarRating.ToDictionary(d => (int)d.Key, d => d.Value)); writer.Write(beatmap.CatchStarRating.ToDictionary(d => (int)d.Key, d => d.Value)); writer.Write(beatmap.ManiaStarRating.ToDictionary(d => (int)d.Key, d => d.Value)); } writer.Write(beatmap.DrainTime); writer.Write(beatmap.TotalTime); writer.Write(beatmap.AudioPreviewTime); writer.Write(beatmap.TimingPoints.Count); for (int j = 0; j < beatmap.TimingPoints.Count; j++) { writer.Write(beatmap.TimingPoints[j].BPM); writer.Write(beatmap.TimingPoints[j].Offset); writer.Write(beatmap.TimingPoints[j].Inherited); } writer.Write(beatmap.BeatmapId); writer.Write(beatmap.BeatmapSetId); writer.Write(beatmap.ThreadId); writer.Write((byte)beatmap.StandardGrade); writer.Write((byte)beatmap.TaikoGrade); writer.Write((byte)beatmap.CatchGrade); writer.Write((byte)beatmap.ManiaGrade); writer.Write(beatmap.LocalOffset); writer.Write(beatmap.StackLeniency); writer.Write((byte)beatmap.Ruleset); writer.Write(beatmap.Source); writer.Write(beatmap.Tags); writer.Write(beatmap.OnlineOffset); writer.Write(beatmap.TitleFont); writer.Write(beatmap.IsUnplayed); writer.Write(beatmap.LastPlayed); writer.Write(beatmap.IsOsz2); writer.Write(beatmap.FolderName); writer.Write(beatmap.LastCheckedAgainstOsuRepo); writer.Write(beatmap.IgnoreBeatmapSound); writer.Write(beatmap.IgnoreBeatmapSkin); writer.Write(beatmap.DisableStoryboard); writer.Write(beatmap.DisableVideo); writer.Write(beatmap.VisualOverride); if (db.OsuVersion < 20140609) { writer.Write((short)0); //let's write 0 here for now } writer.Write(0); //and here writer.Write(beatmap.ManiaScrollSpeed); } writer.Write((int)db.Permissions); } }
private async ValueTask UpdateDatabase(OsuDatabase currentInfo, OsuDatabase newInfo, List <OsuDatabaseBeatmap> beatmaps, CancellationToken token = default) { Logger.LogInformation("Updating database..."); currentInfo.AccountLocked = newInfo.AccountLocked; currentInfo.BeatmapCount = newInfo.BeatmapCount; currentInfo.FolderCount = newInfo.FolderCount; currentInfo.Permission = newInfo.Permission; currentInfo.PlayerName = newInfo.PlayerName; currentInfo.UnlockedAt = newInfo.UnlockedAt; currentInfo.Version = newInfo.Version; Db.Update(currentInfo); await Db.SaveChangesAsync(); // these properties temporarily move out of the comprasion fields var emitSet = new HashSet <string>() { nameof(OsuDatabaseBeatmap.Id), nameof(OsuDatabaseBeatmap.OsuDatabaseId), nameof(OsuDatabaseBeatmap.OsuDatabase), nameof(OsuDatabaseBeatmap.StarRatings), nameof(OsuDatabaseBeatmap.OsuDatabaseTimings), nameof(OsuDatabaseBeatmap.LatestModifiedAt), nameof(OsuDatabaseBeatmap.LatestPlayedAt), nameof(OsuDatabaseBeatmap.LatestUpdateAt), }; var currentModifiedCount = 0; foreach (var beatmap in beatmaps) { if (token.IsCancellationRequested) { return; } // query exist record basic on FileName and FolderName var currentBeatmaps = await Db .OsuDatabaseBeatmap .Where((b) => (b.FileName == beatmap.FileName && b.FolderName == beatmap.FolderName) || (b.BeatmapSetId > 0 && b.BeatmapId > 0 && b.BeatmapSetId == beatmap.BeatmapSetId && b.BeatmapId == beatmap.BeatmapId)) .ToListAsync(); // no records that exist, save to database directly if (currentBeatmaps.Count == 0) { beatmap.OsuDatabaseId = currentInfo.Id; Logger.LogInformation($"Found new beatmap {beatmap.Artist} - {beatmap.Title} ({beatmap.FolderName}\\{beatmap.FileName})"); await Db.AddAsync(beatmap); } // exist 1 record, update else if (currentBeatmaps.Count == 1) { var currentBeatmap = currentBeatmaps[0]; if (Db.Entry(currentBeatmap).Update(beatmap, emitSet) > 0) { Logger.LogInformation($"Update beatmap {beatmap.Artist} - {beatmap.Title} ({beatmap.FolderName}\\{beatmap.FileName})"); } } // exist more than one record, delete old records and create again else { var corrupted = Db .OsuDatabaseBeatmap .Where((b) => b.FileName == beatmap.FileName && b.FolderName == beatmap.FolderName); Db.OsuDatabaseBeatmap.RemoveRange(corrupted); beatmap.OsuDatabaseId = currentInfo.Id; await Db.AddAsync(beatmap); Logger.LogInformation($"A corrupted data detected. Path = {beatmap.FolderName}\\{beatmap.FileName}"); } if (++currentModifiedCount % 500 == 0) { await Db.SaveChangesAsync(); } } Logger.LogInformation($"Update complete."); }
private IEnumerable <OsuDatabaseBeatmap> ReadBeatmap(OsuDatabase db) { for (int i = 0; i < db.BeatmapCount; i++) { var beatmap = new OsuDatabaseBeatmap(); if (db.Version < 20191106) { beatmap.BytesOfBeatmapEntry = Reader.ReadInt32(); } beatmap.Artist = Reader.ReadOsuString(); beatmap.ArtistUnicode = Reader.ReadOsuString(); beatmap.Title = Reader.ReadOsuString(); beatmap.TitleUnicode = Reader.ReadOsuString(); beatmap.Creator = Reader.ReadOsuString(); beatmap.Difficult = Reader.ReadOsuString(); beatmap.AudioFileName = Reader.ReadOsuString(); beatmap.MD5Hash = Reader.ReadOsuString(); beatmap.FileName = Reader.ReadOsuString(); beatmap.RankStatus = (OsuGameBeatmapRankStatus)Reader.ReadByte(); beatmap.CircleCount = Reader.ReadInt16(); beatmap.SliderCount = Reader.ReadInt16(); beatmap.SpinnerCount = Reader.ReadInt16(); beatmap.LatestModifiedAt = Reader.ReadDateTime(); beatmap.ApproachRate = db.Version >= 20140609 ? Reader.ReadSingle() : Reader.ReadByte(); beatmap.CircleSize = db.Version >= 20140609 ? Reader.ReadSingle() : Reader.ReadByte(); beatmap.HPDrain = db.Version >= 20140609 ? Reader.ReadSingle() : Reader.ReadByte(); beatmap.OverallDifficulty = db.Version >= 20140609 ? Reader.ReadSingle() : Reader.ReadByte(); beatmap.SliderVelocity = Reader.ReadDouble(); if (db.Version >= 20140609) { beatmap.StarRatings = ReadStarRatings(beatmap).ToList(); } beatmap.DrainTime = Reader.ReadInt32(); beatmap.TotalTime = Reader.ReadInt32(); beatmap.AudioPreviewTime = Reader.ReadInt32(); beatmap.TimingPointCount = Reader.ReadInt32(); beatmap.OsuDatabaseTimings = ReadBeatmapTimings(beatmap).ToList(); beatmap.BeatmapId = Reader.ReadInt32(); beatmap.BeatmapSetId = Reader.ReadInt32(); beatmap.ThreadId = Reader.ReadInt32(); beatmap.StandardRankRating = (OsuGameRankRating)Reader.ReadByte(); beatmap.TaikoRankRating = (OsuGameRankRating)Reader.ReadByte(); beatmap.CatchTheBeatRankRating = (OsuGameRankRating)Reader.ReadByte(); beatmap.ManiaRankRating = (OsuGameRankRating)Reader.ReadByte(); beatmap.LocalOffset = Reader.ReadInt16(); beatmap.StackLeniency = Reader.ReadSingle(); beatmap.RuleSet = (OsuGameRuleSet)Reader.ReadByte(); beatmap.Source = Reader.ReadOsuString(); beatmap.Tags = Reader.ReadOsuString(); beatmap.OnlineOffset = Reader.ReadInt16(); beatmap.TitleFont = Reader.ReadOsuString(); beatmap.NotPlayed = Reader.ReadBoolean(); beatmap.LatestPlayedAt = Reader.ReadDateTime(); beatmap.IsOsz2 = Reader.ReadBoolean(); beatmap.FolderName = Reader.ReadOsuString(); beatmap.LatestPlayedAt = Reader.ReadDateTime(); beatmap.BeatmapSoundIgnored = Reader.ReadBoolean(); beatmap.BeatmapSkinIgnored = Reader.ReadBoolean(); beatmap.StoryboardDisabled = Reader.ReadBoolean(); beatmap.VideoDisabled = Reader.ReadBoolean(); beatmap.VisualOverrided = Reader.ReadBoolean(); if (db.Version < 20140609) { Reader.BaseStream.Seek(sizeof(short), SeekOrigin.Current); } Reader.BaseStream.Seek(sizeof(int), SeekOrigin.Current); beatmap.ManiaScrollSpeed = Reader.ReadByte(); yield return(beatmap); } }
public OsuDatabase ReadOsuDatabase(ref OsuDatabase db) { db.Beatmaps = ReadBeatmap(db).ToList(); db.Permission = (OsuGameBeatmapPermission)Reader.ReadInt32(); return(db); }