From db443babb6e9281525c65cda4cf71db065eddbb0 Mon Sep 17 00:00:00 2001 From: HoLLy Date: Sun, 7 Oct 2018 19:15:42 +0200 Subject: [PATCH 1/6] Fix database entries using platform-specific path separator --- osu.Game/Database/ArchiveModelManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 723bb90e7e..3686b702c4 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -404,7 +404,7 @@ namespace osu.Game.Database using (Stream s = reader.GetStream(file)) fileInfos.Add(new TFileModel { - Filename = FileSafety.PathSanitise(file), + Filename = FileSafety.PathStandardise(file), FileInfo = files.Add(s) }); From 97f86193018b9e15bac824a1b737580ab643ab83 Mon Sep 17 00:00:00 2001 From: HoLLy Date: Sun, 7 Oct 2018 21:07:30 +0200 Subject: [PATCH 2/6] Add database migration for standardizing paths --- ...0181007180454_StandardizePaths.Designer.cs | 380 ++++++++++++++++++ .../20181007180454_StandardizePaths.cs | 42 ++ .../Migrations/OsuDbContextModelSnapshot.cs | 2 +- 3 files changed, 423 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs create mode 100644 osu.Game/Migrations/20181007180454_StandardizePaths.cs diff --git a/osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs b/osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs new file mode 100644 index 0000000000..b387a45ecf --- /dev/null +++ b/osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs @@ -0,0 +1,380 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using osu.Game.Database; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20181007180454_StandardizePaths")] + partial class StandardizePaths + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.3-rtm-32065"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("DistanceSpacing"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("Status"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash"); + + b.HasIndex("MD5Hash"); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.Property("Status"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntKey") + .HasColumnName("Key"); + + b.Property("RulesetID"); + + b.Property("StringValue") + .HasColumnName("Value"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.Property("ShortName"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.HasIndex("ShortName") + .IsUnique(); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("SkinInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("SkinInfoID"); + + b.ToTable("SkinFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Creator"); + + b.Property("DeletePending"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.ToTable("SkinInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Files") + .HasForeignKey("SkinInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20181007180454_StandardizePaths.cs b/osu.Game/Migrations/20181007180454_StandardizePaths.cs new file mode 100644 index 0000000000..08b34507ce --- /dev/null +++ b/osu.Game/Migrations/20181007180454_StandardizePaths.cs @@ -0,0 +1,42 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using System.IO; + +namespace osu.Game.Migrations +{ + public partial class StandardizePaths : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + string sanitized = Path.DirectorySeparatorChar.ToString(); + string standardized = "/"; + + // If we are not on Windows, no changes are needed. + if (string.Equals(standardized, sanitized, StringComparison.Ordinal)) + return; + + // Escaping \ does not seem to be needed. + migrationBuilder.Sql($"UPDATE `BeatmapInfo` SET `Path` = REPLACE(`Path`, '{sanitized}', '{standardized}')"); + migrationBuilder.Sql($"UPDATE `BeatmapMetadata` SET `AudioFile` = REPLACE(`AudioFile`, '{sanitized}', '{standardized}')"); + migrationBuilder.Sql($"UPDATE `BeatmapMetadata` SET `BackgroundFile` = REPLACE(`BackgroundFile`, '{sanitized}', '{standardized}')"); + migrationBuilder.Sql($"UPDATE `BeatmapSetFileInfo` SET `Filename` = REPLACE(`Filename`, '{sanitized}', '{standardized}')"); + migrationBuilder.Sql($"UPDATE `SkinFileInfo` SET `Filename` = REPLACE(`Filename`, '{sanitized}', '{standardized}')"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + string sanitized = Path.DirectorySeparatorChar.ToString(); + string standardized = "/"; + + // If we are not on Windows, no changes are needed. + if (string.Equals(standardized, sanitized, StringComparison.Ordinal)) + return; + + migrationBuilder.Sql($"UPDATE `BeatmapInfo` SET `Path` = REPLACE(`Path`, '{standardized}', '{sanitized}')"); + migrationBuilder.Sql($"UPDATE `BeatmapMetadata` SET `AudioFile` = REPLACE(`AudioFile`, '{standardized}', '{sanitized}')"); + migrationBuilder.Sql($"UPDATE `BeatmapMetadata` SET `BackgroundFile` = REPLACE(`BackgroundFile`, '{standardized}', '{sanitized}')"); + migrationBuilder.Sql($"UPDATE `BeatmapSetFileInfo` SET `Filename` = REPLACE(`Filename`, '{standardized}', '{sanitized}')"); + migrationBuilder.Sql($"UPDATE `SkinFileInfo` SET `Filename` = REPLACE(`Filename`, '{standardized}', '{sanitized}')"); + } + } +} diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs index fde5c9fd82..663676a6d3 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -14,7 +14,7 @@ namespace osu.Game.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.1.2-rtm-30932"); + .HasAnnotation("ProductVersion", "2.1.3-rtm-32065"); modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => { From b35f88b8ba460be6d7d4a36e3c5761a1a2938bef Mon Sep 17 00:00:00 2001 From: HoLLy Date: Tue, 9 Oct 2018 17:49:18 +0200 Subject: [PATCH 3/6] Standardize AudioFile and BackgroundImage paths --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 181d17932d..3030fcb9da 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -5,6 +5,7 @@ using System; using System.Globalization; using System.IO; using System.Linq; +using osu.Framework.IO.File; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Beatmaps.ControlPoints; @@ -100,7 +101,7 @@ namespace osu.Game.Beatmaps.Formats switch (pair.Key) { case @"AudioFilename": - metadata.AudioFile = pair.Value; + metadata.AudioFile = FileSafety.PathStandardise(pair.Value); break; case @"AudioLeadIn": beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value); @@ -256,7 +257,7 @@ namespace osu.Game.Beatmaps.Formats { case EventType.Background: string filename = split[2].Trim('"'); - beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; + beatmap.BeatmapInfo.Metadata.BackgroundFile = FileSafety.PathStandardise(filename); break; case EventType.Break: var breakEvent = new BreakPeriod From 710b0a46648e2a005ac1d17a539035ceac5a795f Mon Sep 17 00:00:00 2001 From: HoLLy Date: Fri, 12 Oct 2018 15:30:24 +0200 Subject: [PATCH 4/6] Remove unnecessary PathSanitise call --- osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index a73a32325a..375c0b29c0 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -298,6 +298,6 @@ namespace osu.Game.Beatmaps.Formats } } - private string cleanFilename(string path) => FileSafety.PathStandardise(FileSafety.PathSanitise(path.Trim('\"'))); + private string cleanFilename(string path) => FileSafety.PathStandardise(path.Trim('"')); } } From eb9199e07ae40fbf73cb9a447ea17996f49b6799 Mon Sep 17 00:00:00 2001 From: HoLLy Date: Tue, 16 Oct 2018 08:29:47 +0200 Subject: [PATCH 5/6] Run database migration unconditionally, remove downwards migration --- .../20181007180454_StandardizePaths.cs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/osu.Game/Migrations/20181007180454_StandardizePaths.cs b/osu.Game/Migrations/20181007180454_StandardizePaths.cs index 08b34507ce..e1b46dd018 100644 --- a/osu.Game/Migrations/20181007180454_StandardizePaths.cs +++ b/osu.Game/Migrations/20181007180454_StandardizePaths.cs @@ -11,10 +11,6 @@ namespace osu.Game.Migrations string sanitized = Path.DirectorySeparatorChar.ToString(); string standardized = "/"; - // If we are not on Windows, no changes are needed. - if (string.Equals(standardized, sanitized, StringComparison.Ordinal)) - return; - // Escaping \ does not seem to be needed. migrationBuilder.Sql($"UPDATE `BeatmapInfo` SET `Path` = REPLACE(`Path`, '{sanitized}', '{standardized}')"); migrationBuilder.Sql($"UPDATE `BeatmapMetadata` SET `AudioFile` = REPLACE(`AudioFile`, '{sanitized}', '{standardized}')"); @@ -25,18 +21,6 @@ namespace osu.Game.Migrations protected override void Down(MigrationBuilder migrationBuilder) { - string sanitized = Path.DirectorySeparatorChar.ToString(); - string standardized = "/"; - - // If we are not on Windows, no changes are needed. - if (string.Equals(standardized, sanitized, StringComparison.Ordinal)) - return; - - migrationBuilder.Sql($"UPDATE `BeatmapInfo` SET `Path` = REPLACE(`Path`, '{standardized}', '{sanitized}')"); - migrationBuilder.Sql($"UPDATE `BeatmapMetadata` SET `AudioFile` = REPLACE(`AudioFile`, '{standardized}', '{sanitized}')"); - migrationBuilder.Sql($"UPDATE `BeatmapMetadata` SET `BackgroundFile` = REPLACE(`BackgroundFile`, '{standardized}', '{sanitized}')"); - migrationBuilder.Sql($"UPDATE `BeatmapSetFileInfo` SET `Filename` = REPLACE(`Filename`, '{standardized}', '{sanitized}')"); - migrationBuilder.Sql($"UPDATE `SkinFileInfo` SET `Filename` = REPLACE(`Filename`, '{standardized}', '{sanitized}')"); } } } From 3f185a44defeabc22d662e0938553cf594230098 Mon Sep 17 00:00:00 2001 From: HoLLy Date: Thu, 18 Oct 2018 08:29:12 +0200 Subject: [PATCH 6/6] Fix Windows-style path separators not being migrated on Unix systems --- .../Migrations/20181007180454_StandardizePaths.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Migrations/20181007180454_StandardizePaths.cs b/osu.Game/Migrations/20181007180454_StandardizePaths.cs index e1b46dd018..274b8030a9 100644 --- a/osu.Game/Migrations/20181007180454_StandardizePaths.cs +++ b/osu.Game/Migrations/20181007180454_StandardizePaths.cs @@ -8,15 +8,15 @@ namespace osu.Game.Migrations { protected override void Up(MigrationBuilder migrationBuilder) { - string sanitized = Path.DirectorySeparatorChar.ToString(); + string windowsStyle = @"\"; string standardized = "/"; // Escaping \ does not seem to be needed. - migrationBuilder.Sql($"UPDATE `BeatmapInfo` SET `Path` = REPLACE(`Path`, '{sanitized}', '{standardized}')"); - migrationBuilder.Sql($"UPDATE `BeatmapMetadata` SET `AudioFile` = REPLACE(`AudioFile`, '{sanitized}', '{standardized}')"); - migrationBuilder.Sql($"UPDATE `BeatmapMetadata` SET `BackgroundFile` = REPLACE(`BackgroundFile`, '{sanitized}', '{standardized}')"); - migrationBuilder.Sql($"UPDATE `BeatmapSetFileInfo` SET `Filename` = REPLACE(`Filename`, '{sanitized}', '{standardized}')"); - migrationBuilder.Sql($"UPDATE `SkinFileInfo` SET `Filename` = REPLACE(`Filename`, '{sanitized}', '{standardized}')"); + migrationBuilder.Sql($"UPDATE `BeatmapInfo` SET `Path` = REPLACE(`Path`, '{windowsStyle}', '{standardized}')"); + migrationBuilder.Sql($"UPDATE `BeatmapMetadata` SET `AudioFile` = REPLACE(`AudioFile`, '{windowsStyle}', '{standardized}')"); + migrationBuilder.Sql($"UPDATE `BeatmapMetadata` SET `BackgroundFile` = REPLACE(`BackgroundFile`, '{windowsStyle}', '{standardized}')"); + migrationBuilder.Sql($"UPDATE `BeatmapSetFileInfo` SET `Filename` = REPLACE(`Filename`, '{windowsStyle}', '{standardized}')"); + migrationBuilder.Sql($"UPDATE `SkinFileInfo` SET `Filename` = REPLACE(`Filename`, '{windowsStyle}', '{standardized}')"); } protected override void Down(MigrationBuilder migrationBuilder)