From f74214d3c4ee5f7b92fb12d02da11700fd30bea8 Mon Sep 17 00:00:00 2001 From: TheBoxyBear <15822255+TheBoxyBear@users.noreply.github.com> Date: Thu, 18 Jan 2024 12:52:43 -0500 Subject: [PATCH 1/7] Move Vocals to ChartTools.Lyrics --- ChartTools/Instruments/InstrumentSet.cs | 1 + ChartTools/Instruments/Vocals.cs | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ChartTools/Instruments/InstrumentSet.cs b/ChartTools/Instruments/InstrumentSet.cs index 361b57d7..4365fe28 100644 --- a/ChartTools/Instruments/InstrumentSet.cs +++ b/ChartTools/Instruments/InstrumentSet.cs @@ -1,4 +1,5 @@ using ChartTools.Extensions.Linq; +using ChartTools.Lyrics; using System.Collections; diff --git a/ChartTools/Instruments/Vocals.cs b/ChartTools/Instruments/Vocals.cs index 842fd8b9..3be197db 100644 --- a/ChartTools/Instruments/Vocals.cs +++ b/ChartTools/Instruments/Vocals.cs @@ -1,6 +1,4 @@ -using ChartTools.Lyrics; - -namespace ChartTools; +namespace ChartTools.Lyrics; public record Vocals : Instrument { From 7491b08cc120174481037f510decb670a46862ff Mon Sep 17 00:00:00 2001 From: TheBoxyBear <15822255+TheBoxyBear@users.noreply.github.com> Date: Sun, 14 Jul 2024 15:55:30 -0400 Subject: [PATCH 2/7] #70 Model list --- ChartTools/IO/ComponentList.cs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 ChartTools/IO/ComponentList.cs diff --git a/ChartTools/IO/ComponentList.cs b/ChartTools/IO/ComponentList.cs new file mode 100644 index 00000000..9da704bc --- /dev/null +++ b/ChartTools/IO/ComponentList.cs @@ -0,0 +1,29 @@ +namespace ChartTools.IO; + +[Flags] +public enum DifficultySet : byte +{ + None = 0, + Easy = 1 << 0, + Medium = 1 << 1, + Hard = 1 << 2, + Expert = 1 << 3, + All = Easy | Medium | Hard | Expert +}; + +public record ComponentList +{ + public bool Metadata { get; set; } + public bool SyncTrack { get; set; } + public bool GlobalEvents { get; set; } + + public DifficultySet Drums { get; set; } + public DifficultySet LeadGuitar { get; set; } + public DifficultySet CoopGuitar { get; set; } + public DifficultySet RythmGuitar { get; set; } + public DifficultySet Bass { get; set; } + public DifficultySet GHLGuitar { get; set; } + public DifficultySet GHLBass { get; set; } + public DifficultySet Keys { get; set; } + public DifficultySet Vocals { get; set; } +} From dadac09d7b108d81c029dc02812ed0c73b5e54ee Mon Sep 17 00:00:00 2001 From: TheBoxyBear <15822255+TheBoxyBear@users.noreply.github.com> Date: Sun, 14 Jul 2024 19:40:22 -0400 Subject: [PATCH 3/7] Support component list for reading --- ChartTools/IO/Chart/ChartFile.cs | 258 ++++++------------ ChartTools/IO/Chart/ChartFileReader.cs | 38 ++- ChartTools/IO/Chart/ChartFormatting.cs | 33 ++- ChartTools/IO/Chart/ChartSectionSet.cs | 1 - .../Sessions/ChartReadingSession.cs | 3 +- .../Configuration/Sessions/ChartSession.cs | 2 +- ChartTools/IO/ComponentList.cs | 179 ++++++++++-- ChartTools/IO/Configuration/Session.cs | 6 +- ChartTools/IO/FileReader.cs | 6 +- ChartTools/IO/TextFileReader.cs | 6 +- 10 files changed, 325 insertions(+), 207 deletions(-) diff --git a/ChartTools/IO/Chart/ChartFile.cs b/ChartTools/IO/Chart/ChartFile.cs index a1fbf848..4069e62b 100644 --- a/ChartTools/IO/Chart/ChartFile.cs +++ b/ChartTools/IO/Chart/ChartFile.cs @@ -43,36 +43,6 @@ public static class ChartFile #region Reading #region Song - /// - /// Creates a for parsing a section based on the header. - /// - /// - private static ChartParser GetSongParser(string header, ChartReadingSession session) - { - switch (header) - { - case ChartFormatting.MetadataHeader: - return new MetadataParser(); - case ChartFormatting.GlobalEventHeader: - return new GlobalEventParser(session); - case ChartFormatting.SyncTrackHeader: - return new SyncTrackParser(session); - default: - if (drumsTrackHeaders.TryGetValue(header, out Difficulty diff)) - return new DrumsTrackParser(diff, session, header); - else if (ghlTrackHeaders.TryGetValue(header, out (Difficulty, GHLInstrumentIdentity) ghlTuple)) - return new GHLTrackParser(ghlTuple.Item1, ghlTuple.Item2, session, header); - else if (standardTrackHeaders.TryGetValue(header, out (Difficulty, StandardInstrumentIdentity) standardTuple)) - return new StandardTrackParser(standardTuple.Item1, standardTuple.Item2, session, header); - else - { - return session.Configuration.UnknownSectionPolicy == UnknownSectionPolicy.ThrowException - ? throw new Exception($"Unknown section with header \"{header}\". Consider using {UnknownSectionPolicy.Store} to avoid this error.") - : new UnknownSectionParser(session, header); - } - } - } - /// /// Combines the results from the parsers of a into a . /// @@ -92,26 +62,43 @@ private static Song CreateSongFromReader(ChartFileReader reader) /// public static Song ReadSong(string path, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) { - var session = new ChartReadingSession(config, formatting ?? new()); - var reader = new ChartFileReader(path, header => GetSongParser(header, session)); + var session = new ChartReadingSession(ComponentList.Full, config, formatting); + var reader = new ChartFileReader(path, session); reader.Read(); return CreateSongFromReader(reader); } - /// - /// - /// - /// public static async Task ReadSongAsync(string path, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) { - var session = new ChartReadingSession(config, formatting ?? new()); - var reader = new ChartFileReader(path, header => GetSongParser(header, session)); + var session = new ChartReadingSession(ComponentList.Full, config, formatting); + var reader = new ChartFileReader(path, session); + + await reader.ReadAsync(cancellationToken); + return CreateSongFromReader(reader); + } + + public static Song ReadComponents(string path, ComponentList components, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) + { + + var session = new ChartReadingSession(components, config, formatting); + var reader = new ChartFileReader(path, session); + + reader.Read(); + return CreateSongFromReader(reader); + } + + public static async Task ReadComponents(string path, ComponentList components, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) + { + + var session = new ChartReadingSession(components, config, formatting); + var reader = new ChartFileReader(path, session); await reader.ReadAsync(cancellationToken); return CreateSongFromReader(reader); } #endregion + #region Instruments /// /// Combines the results from the parsers in a into an instrument. @@ -135,9 +122,6 @@ public static async Task ReadSongAsync(string path, ChartReadingConfigurat /// Path of the file to read /// Instrument to read /// - /// - /// - /// public static Instrument? ReadInstrument(string path, InstrumentIdentity instrument, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) { if (instrument == InstrumentIdentity.Drums) @@ -158,6 +142,7 @@ public static async Task ReadSongAsync(string path, ChartReadingConfigurat ? await ReadInstrumentAsync(path, (StandardInstrumentIdentity)instrument, config, formatting, cancellationToken) : throw new UndefinedEnumException(instrument); } + #region Vocals /// /// Reads vocals from the global events in a chart file. @@ -168,7 +153,8 @@ public static async Task ReadSongAsync(string path, ChartReadingConfigurat /// Path of the file to read public static Vocals? ReadVocals(string path) => BuildVocals(ReadGlobalEvents(path)); public static async Task ReadVocalsAsync(string path, CancellationToken cancellationToken = default) => BuildVocals(await ReadGlobalEventsAsync(path, cancellationToken)); - private static Vocals? BuildVocals(List events) + + private static Vocals? BuildVocals(IList events) { var lyrics = events.GetLyrics().ToArray(); @@ -186,10 +172,8 @@ public static async Task ReadSongAsync(string path, ChartReadingConfigurat return instument; } #endregion + #region Drums - private static DrumsTrackParser? GetAnyDrumsTrackParser(string header, ChartReadingSession session) => drumsTrackHeaders.TryGetValue(header, out Difficulty difficulty) - ? new(difficulty, session, header) - : null; /// /// Reads drums from a chart file. /// @@ -201,25 +185,23 @@ public static async Task ReadSongAsync(string path, ChartReadingConfigurat /// public static Drums? ReadDrums(string path, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) { - var session = new ChartReadingSession(config, formatting ?? new()); - var reader = new ChartFileReader(path, header => GetAnyDrumsTrackParser(header, session)); + var session = new ChartReadingSession(new(InstrumentIdentity.Drums), config, formatting); + var reader = new ChartFileReader(path, session); reader.Read(); return CreateInstrumentFromReader(reader); } public static async Task ReadDrumsAsync(string path, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) { - var session = new ChartReadingSession(config, formatting ?? new()); - var reader = new ChartFileReader(path, header => GetAnyDrumsTrackParser(header, session)); + var session = new ChartReadingSession(new(InstrumentIdentity.Drums), config, formatting); + var reader = new ChartFileReader(path, session); await reader.ReadAsync(cancellationToken); return CreateInstrumentFromReader(reader); } #endregion + #region GHL - private static GHLTrackParser? GetAnyGHLTrackParser(string header, GHLInstrumentIdentity instrument, ChartReadingSession session) => ghlTrackHeaders.TryGetValue(header, out (Difficulty, GHLInstrumentIdentity) tuple) && tuple.Item2 == instrument - ? new(tuple.Item1, tuple.Item2, session, header) - : null; /// /// Reads a Guitar Hero Live instrument from a chart file. /// @@ -227,63 +209,45 @@ public static async Task ReadSongAsync(string path, ChartReadingConfigurat /// if the file has no data for the given instrument /// /// Path of the file to read - /// public static GHLInstrument? ReadInstrument(string path, GHLInstrumentIdentity instrument, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) { - Validator.ValidateEnum(instrument); - - var session = new ChartReadingSession(config, formatting ?? new()); - var reader = new ChartFileReader(path, header => GetAnyGHLTrackParser(header, instrument, session)); + var session = new ChartReadingSession(new(instrument), config, formatting); + var reader = new ChartFileReader(path, session); reader.Read(); return CreateInstrumentFromReader(reader); } public static async Task ReadInstrumentAsync(string path, GHLInstrumentIdentity instrument, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) { - Validator.ValidateEnum(instrument); - - var session = new ChartReadingSession(config, formatting ?? new()); - var reader = new ChartFileReader(path, header => GetAnyGHLTrackParser(header, instrument, session)); + var session = new ChartReadingSession(new(instrument), config, formatting); + var reader = new ChartFileReader(path, session); await reader.ReadAsync(cancellationToken); return CreateInstrumentFromReader(reader); } #endregion + #region Standard - private static StandardTrackParser? GetAnyStandardTrackParser(string header, StandardInstrumentIdentity instrument, ChartReadingSession session) => standardTrackHeaders.TryGetValue(header, out (Difficulty, StandardInstrumentIdentity) tuple) && tuple.Item2 == instrument - ? new(tuple.Item1, tuple.Item2, session, header) - : null; - /// - /// - /// - /// public static StandardInstrument? ReadInstrument(string path, StandardInstrumentIdentity instrument, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) { - Validator.ValidateEnum(instrument); - - var session = new ChartReadingSession(config, formatting ?? new()); - var reader = new ChartFileReader(path, header => GetAnyStandardTrackParser(header, instrument, session)); + var session = new ChartReadingSession(new(instrument), config, formatting); + var reader = new ChartFileReader(path, session); reader.Read(); return CreateInstrumentFromReader(reader); } - /// - /// - /// - /// - /// + public static async Task ReadInstrumentAsync(string path, StandardInstrumentIdentity instrument, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) { - Validator.ValidateEnum(instrument); - - var session = new ChartReadingSession(config, formatting ?? new()); - var reader = new ChartFileReader(path, header => GetAnyStandardTrackParser(header, instrument, session)); + var session = new ChartReadingSession(new(instrument), config, formatting); + var reader = new ChartFileReader(path, session); await reader.ReadAsync(cancellationToken); return CreateInstrumentFromReader(reader); } #endregion #endregion + #region Tracks /// /// @@ -319,33 +283,19 @@ public static async Task ReadTrackAsync(string path, InstrumentIdentity i throw new UndefinedEnumException(instrument); } #region Drums - /// - /// Creates a is the header matches the requested standard track, otherwise . - /// - /// Header of the part - /// Header to compare against - /// Difficulty identity to provide the parser - /// Session to provide the parser - private static DrumsTrackParser? GetDrumsTrackParser(string header, string seekedHeader, Difficulty difficulty, ChartReadingSession session) => header == seekedHeader ? new(difficulty, session, header) : null; - /// - /// Headers for drums tracks - /// - private static readonly Dictionary drumsTrackHeaders = EnumCache.Values.ToDictionary(diff => ChartFormatting.Header(ChartFormatting.DrumsHeaderName, diff)); /// /// /// /// public static Track ReadDrumsTrack(string path, Difficulty difficulty, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) { - Validator.ValidateEnum(difficulty); - - var seekedHeader = ChartFormatting.Header(InstrumentIdentity.Drums, difficulty); - var session = new ChartReadingSession(config, formatting ?? new()); - var reader = new ChartFileReader(path, header => GetDrumsTrackParser(header, seekedHeader, difficulty, session)); + var session = new ChartReadingSession(new(InstrumentIdentity.Drums, difficulty), config, formatting); + var reader = new ChartFileReader(path, session); reader.Read(); return reader.Parsers.TryGetFirstOfType(out DrumsTrackParser? parser) ? parser!.Result! : new(); } + /// /// /// @@ -353,30 +303,16 @@ public static Track ReadDrumsTrack(string path, Difficulty difficult /// public static async Task> ReadDrumsTrackAsync(string path, Difficulty difficulty, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) { - Validator.ValidateEnum(difficulty); - - var seekedHeader = ChartFormatting.Header(ChartFormatting.DrumsHeaderName, difficulty); - var session = new ChartReadingSession(config, formatting ?? new()); - var reader = new ChartFileReader(path, header => GetDrumsTrackParser(header, seekedHeader, difficulty, session)); + var session = new ChartReadingSession(new(InstrumentIdentity.Drums, difficulty), config, formatting); + var reader = new ChartFileReader(path, session); await reader.ReadAsync(cancellationToken); return reader.Parsers.TryGetFirstOfType(out DrumsTrackParser? parser) ? parser!.Result! : new(); } #endregion + #region GHL - /// - /// Creates a is the header matches the requested standard track, otherwise . - /// - /// Header of the part - /// Header to compare against - /// Instrument identity to provide the parser - /// Difficulty identity to provide the parser - /// Session to provide the parser - private static GHLTrackParser? GetGHLTrackParser(string header, string seekedHeader, GHLInstrumentIdentity instrument, Difficulty difficulty, ChartReadingSession session) => header == seekedHeader ? new(difficulty, instrument, session, header) : null; - /// - /// Headers for GHL tracks - /// - private static readonly Dictionary ghlTrackHeaders = GetTrackCombinations(Enum.GetValues()).ToDictionary(tuple => ChartFormatting.Header(tuple.instrument, tuple.difficulty)); + /// /// /// @@ -384,16 +320,13 @@ public static async Task> ReadDrumsTrackAsync(string path, Dif /// public static Track ReadTrack(string path, GHLInstrumentIdentity instrument, Difficulty difficulty, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) { - Validator.ValidateEnum(instrument); - Validator.ValidateEnum(difficulty); - - var seekedHeader = ChartFormatting.Header(instrument, difficulty); - var session = new ChartReadingSession(config, formatting ?? new()); - var reader = new ChartFileReader(path, header => GetGHLTrackParser(header, seekedHeader, instrument, difficulty, session)); + var session = new ChartReadingSession(new(instrument, difficulty), config, formatting); + var reader = new ChartFileReader(path, session); reader.Read(); return reader.Parsers.TryGetFirstOfType(out GHLTrackParser? parser) ? parser!.Result : new(); } + /// /// /// @@ -402,33 +335,15 @@ public static Track ReadTrack(string path, GHLInstrumentIdentity instr /// public static async Task> ReadTrackAsync(string path, GHLInstrumentIdentity instrument, Difficulty difficulty, ChartReadingConfiguration? config, FormattingRules? formatting = default, CancellationToken cancellationToken = default) { - Validator.ValidateEnum(instrument); - Validator.ValidateEnum(difficulty); - - var seekedHeader = ChartFormatting.Header(instrument, difficulty); - var session = new ChartReadingSession(config ?? DefaultReadConfig, formatting ?? new()); - var reader = new ChartFileReader(path, header => GetGHLTrackParser(header, seekedHeader, instrument, difficulty, session)); + var session = new ChartReadingSession(new(instrument, difficulty), config, formatting); + var reader = new ChartFileReader(path, session); await reader.ReadAsync(cancellationToken); return reader.Parsers.TryGetFirstOfType(out GHLTrackParser? parser) ? parser!.Result : new(); } #endregion - #region Standard - /// - /// Creates a is the header matches the requested standard track, otherwise . - /// - /// Header of the part - /// Header to compare against - /// Instrument identity to provide the parser - /// Difficulty identity to provide the parser - /// Session to provide the parser - private static StandardTrackParser? GetStandardTrackParser(string header, string seekedHeader, StandardInstrumentIdentity instrument, Difficulty difficulty, ChartReadingSession session) => header == seekedHeader ? new(difficulty, instrument, session, header) : null; - - /// - /// Headers for standard tracks - /// - private static readonly Dictionary standardTrackHeaders = GetTrackCombinations(Enum.GetValues()).ToDictionary(tuple => ChartFormatting.Header((InstrumentIdentity)tuple.instrument, tuple.difficulty)); + #region Standard /// /// /// @@ -436,16 +351,13 @@ public static async Task> ReadTrackAsync(string path, GHLInstrum /// public static Track ReadTrack(string path, StandardInstrumentIdentity instrument, Difficulty difficulty, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) { - Validator.ValidateEnum(instrument); - Validator.ValidateEnum(difficulty); - - var seekedHeader = ChartFormatting.Header(instrument, difficulty); - var session = new ChartReadingSession(config, formatting ?? new()); - var reader = new ChartFileReader(path, header => GetStandardTrackParser(header, seekedHeader, instrument, difficulty, session)); + var session = new ChartReadingSession(new(instrument, difficulty), config, formatting); + var reader = new ChartFileReader(path, session); reader.Read(); return reader.Parsers.TryGetFirstOfType(out StandardTrackParser? parser) ? parser!.Result! : new(); } + /// /// /// @@ -455,54 +367,53 @@ public static Track ReadTrack(string path, StandardInstrumentIden /// public static async Task> ReadTrackAsync(string path, StandardInstrumentIdentity instrument, Difficulty difficulty, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) { - Validator.ValidateEnum(instrument); - Validator.ValidateEnum(difficulty); - - var seekedHeader = ChartFormatting.Header(instrument, difficulty); - var session = new ChartReadingSession(config, formatting ?? new()); - var reader = new ChartFileReader(path, header => GetStandardTrackParser(header, seekedHeader, instrument, difficulty, session)); + var session = new ChartReadingSession(new(instrument, difficulty), config, formatting); + var reader = new ChartFileReader(path, session); await reader.ReadAsync(cancellationToken); return reader.Parsers.TryGetFirstOfType(out StandardTrackParser? parser) ? parser!.Result! : new(); } #endregion #endregion + #region Metadata - private static MetadataParser? GetMetadataParser(string header) => header == ChartFormatting.MetadataHeader ? new() : null; /// /// Reads metadata from a chart file. /// /// Path of the file to read public static Metadata ReadMetadata(string path) { - var reader = new ChartFileReader(path, header => GetMetadataParser(header)); + var session = new ChartReadingSession(new() { Metadata = true }, DefaultReadConfig, null); + var reader = new ChartFileReader(path, session); + reader.Read(); return reader.Parsers.TryGetFirstOfType(out MetadataParser? parser) ? parser!.Result : new(); } #endregion - #region Global events - /// - /// Creates a if the header matches the sync track header, otherwise . - /// - private static GlobalEventParser? GetGlobalEventParser(string header) => header == ChartFormatting.GlobalEventHeader ? new(null!) : null; + #region Global events /// /// public static List ReadGlobalEvents(string path) { - var reader = new ChartFileReader(path, GetGlobalEventParser); + var session = new ChartReadingSession(new() { GlobalEvents = true }, DefaultReadConfig, null); + var reader = new ChartFileReader(path, session); + reader.Read(); - return reader.Parsers.TryGetFirstOfType(out GlobalEventParser? parser) ? parser!.Result! : new(); + return reader.Parsers.TryGetFirstOfType(out GlobalEventParser? parser) ? parser!.Result! : []; } + /// /// /// /// public static async Task> ReadGlobalEventsAsync(string path, CancellationToken cancellationToken = default) { - var reader = new ChartFileReader(path, GetGlobalEventParser); + var session = new ChartReadingSession(new() { GlobalEvents = true }, DefaultReadConfig, null); + var reader = new ChartFileReader(path, session); + await reader.ReadAsync(cancellationToken); - return reader.Parsers.TryGetFirstOfType(out GlobalEventParser? parser) ? parser!.Result! : new(); + return reader.Parsers.TryGetFirstOfType(out GlobalEventParser? parser) ? parser!.Result! : []; } /// @@ -519,18 +430,17 @@ public static async Task> ReadGlobalEventsAsync(string path, C /// Token to request cancellation public static async Task> ReadLyricsAsync(string path, CancellationToken cancellationToken = default) => (await ReadGlobalEventsAsync(path, cancellationToken)).GetLyrics(); #endregion + #region Sync track - /// - /// Creates a if the header matches the sync track header, otherwise . - /// - private static SyncTrackParser? GetSyncTrackParser(string header, ChartReadingSession session) => header == ChartFormatting.SyncTrackHeader ? new(session) : null; /// /// /// - public static SyncTrack ReadSyncTrack(string path, ChartReadingConfiguration? config, FormattingRules? formatting = default) + public static SyncTrack ReadSyncTrack(string path, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) { - var reader = new ChartFileReader(path, (header) => GetSyncTrackParser(header, new(config, formatting ?? new()))); + var session = new ChartReadingSession(new() { SyncTrack = true }, config, null); + var reader = new ChartFileReader(path, session); + reader.Read(); return reader.Parsers.TryGetFirstOfType(out SyncTrackParser? syncTrackParser) ? syncTrackParser!.Result! : new(); } @@ -541,9 +451,9 @@ public static SyncTrack ReadSyncTrack(string path, ChartReadingConfiguration? co /// public static async Task ReadSyncTrackAsync(string path, ChartReadingConfiguration? config = default, CancellationToken cancellationToken = default) { - config ??= DefaultReadConfig; + var session = new ChartReadingSession(new() { SyncTrack = true }, config, null); + var reader = new ChartFileReader(path, session); - var reader = new ChartFileReader(path, (header) => GetSyncTrackParser(header, new(config ?? DefaultReadConfig, null))); await reader.ReadAsync(cancellationToken); return reader.Parsers.TryGetFirstOfType(out SyncTrackParser? syncTrackParser) ? syncTrackParser!.Result! : new(); } diff --git a/ChartTools/IO/Chart/ChartFileReader.cs b/ChartTools/IO/Chart/ChartFileReader.cs index d27b3e66..336d1985 100644 --- a/ChartTools/IO/Chart/ChartFileReader.cs +++ b/ChartTools/IO/Chart/ChartFileReader.cs @@ -1,16 +1,48 @@ -using ChartTools.IO.Chart.Parsing; +using ChartTools.IO.Chart.Configuration.Sessions; +using ChartTools.IO.Chart.Parsing; +using ChartTools.IO.Configuration; +using ChartTools.IO.Parsing; namespace ChartTools.IO.Chart; /// /// Reader of text file that sends read lines to subscribers of its events. /// -internal class ChartFileReader : TextFileReader +internal class ChartFileReader(string path, ChartReadingSession session) : TextFileReader(path) { + protected readonly ChartReadingSession session = session; + public override IEnumerable Parsers => base.Parsers.Cast(); public override bool DefinedSectionEnd => true; - public ChartFileReader(string path, Func parserGetter) : base(path, parserGetter) { } + protected override TextParser? GetParser(string header) + { + switch (header) + { + case ChartFormatting.MetadataHeader: + return session.Components.Metadata ? new MetadataParser() : null; + case ChartFormatting.GlobalEventHeader: + return session.Components.GlobalEvents ? new GlobalEventParser(session) : null; + case ChartFormatting.SyncTrackHeader: + return session.Components.SyncTrack ? new SyncTrackParser(session) : null; + default: + if (ChartFormatting.DrumsTrackHeaders.TryGetValue(header, out Difficulty diff)) + return session.Components.Drums.HasFlag(diff.ToSet()) + ? new DrumsTrackParser(diff, session, header) : null; + else if (ChartFormatting.GHLTrackHeaders.TryGetValue(header, out (Difficulty, GHLInstrumentIdentity) ghlTuple)) + return session.Components.Drums.HasFlag(ghlTuple.Item1.ToSet()) + ? new GHLTrackParser(ghlTuple.Item1, ghlTuple.Item2, session, header) : null; + else if (ChartFormatting.StandardTrackHeaders.TryGetValue(header, out (Difficulty, StandardInstrumentIdentity) standardTuple)) + return session.Components.Drums.HasFlag(standardTuple.Item1.ToSet()) + ? new StandardTrackParser(standardTuple.Item1, standardTuple.Item2, session, header) : null; + else + { + return session.Configuration.UnknownSectionPolicy == UnknownSectionPolicy.ThrowException + ? throw new Exception($"Unknown section with header \"{header}\". Consider using {UnknownSectionPolicy.Store} to avoid this error.") + : new UnknownSectionParser(session, header); + } + } + } protected override bool IsSectionStart(string line) => line == "{"; protected override bool IsSectionEnd(string line) => ChartFormatting.IsSectionEnd(line); diff --git a/ChartTools/IO/Chart/ChartFormatting.cs b/ChartTools/IO/Chart/ChartFormatting.cs index a7dceaf2..a5667282 100644 --- a/ChartTools/IO/Chart/ChartFormatting.cs +++ b/ChartTools/IO/Chart/ChartFormatting.cs @@ -1,4 +1,5 @@ -using ChartTools.IO.Chart.Entries; +using ChartTools.Extensions; +using ChartTools.IO.Chart.Entries; namespace ChartTools.IO.Chart; @@ -35,7 +36,7 @@ internal static class ChartFormatting /// /// Part names of without the difficulty /// - public static readonly Dictionary InstrumentHeaderNames = new() + public static readonly IReadOnlyDictionary InstrumentHeaderNames = new Dictionary() { { InstrumentIdentity.Drums, DrumsHeaderName }, { InstrumentIdentity.GHLGuitar, "GHLGuitar" }, @@ -47,6 +48,34 @@ internal static class ChartFormatting { InstrumentIdentity.Keys, "Keyboard" } }; + /// + /// Headers for drums tracks + /// + public static readonly IReadOnlyDictionary DrumsTrackHeaders = + EnumCache.Values + .ToDictionary(diff => Header(DrumsHeaderName, diff)); + + /// + /// Headers for GHL tracks + /// + public static readonly IReadOnlyDictionary GHLTrackHeaders = + GetTrackCombinations(Enum.GetValues()) + .ToDictionary(tuple => Header(tuple.instrument, tuple.difficulty)); + + /// + /// Headers for standard tracks + /// + public static readonly Dictionary StandardTrackHeaders = + GetTrackCombinations(Enum.GetValues()) + .ToDictionary(tuple => Header((InstrumentIdentity)tuple.instrument, tuple.difficulty)); + + /// + /// Gets all the combinations of instruments and difficulties. + /// + /// Enum containing the instruments + private static IEnumerable<(Difficulty difficulty, TInstEnum instrument)> GetTrackCombinations(IEnumerable instruments) + => from difficulty in EnumCache.Values from instrument in instruments select (difficulty, instrument); + public static string Header(Enum instrument, Difficulty difficulty) => Header((InstrumentIdentity)instrument, difficulty); public static string Header(InstrumentIdentity instrument, Difficulty difficulty) => Header(InstrumentHeaderNames[instrument], difficulty); public static string Header(string instrumentName, Difficulty difficulty) => Header(difficulty.ToString() + instrumentName); diff --git a/ChartTools/IO/Chart/ChartSectionSet.cs b/ChartTools/IO/Chart/ChartSectionSet.cs index fc8358ed..3b6aa4b5 100644 --- a/ChartTools/IO/Chart/ChartSectionSet.cs +++ b/ChartTools/IO/Chart/ChartSectionSet.cs @@ -33,5 +33,4 @@ static ChartSection() DefaultReservedHeaders = new(headers); } - public ChartSection() : base() { } } diff --git a/ChartTools/IO/Chart/Configuration/Sessions/ChartReadingSession.cs b/ChartTools/IO/Chart/Configuration/Sessions/ChartReadingSession.cs index 668ac492..96a9cddf 100644 --- a/ChartTools/IO/Chart/Configuration/Sessions/ChartReadingSession.cs +++ b/ChartTools/IO/Chart/Configuration/Sessions/ChartReadingSession.cs @@ -3,7 +3,8 @@ namespace ChartTools.IO.Chart.Configuration.Sessions; -internal class ChartReadingSession(ChartReadingConfiguration? config, FormattingRules? formatting) : ChartSession(formatting) +internal class ChartReadingSession(ComponentList components, ChartReadingConfiguration? config, FormattingRules? formatting) + : ChartSession(components, formatting) { public override ChartReadingConfiguration Configuration { get; } = config ?? ChartFile.DefaultReadConfig; diff --git a/ChartTools/IO/Chart/Configuration/Sessions/ChartSession.cs b/ChartTools/IO/Chart/Configuration/Sessions/ChartSession.cs index 7dd56c68..6327fe44 100644 --- a/ChartTools/IO/Chart/Configuration/Sessions/ChartSession.cs +++ b/ChartTools/IO/Chart/Configuration/Sessions/ChartSession.cs @@ -3,7 +3,7 @@ namespace ChartTools.IO.Chart.Configuration.Sessions; -internal abstract class ChartSession(FormattingRules? formatting) : Session(formatting) +internal abstract class ChartSession(ComponentList components, FormattingRules? formatting) : Session(components, formatting) { public override abstract CommonChartConfiguration Configuration { get; } } diff --git a/ChartTools/IO/ComponentList.cs b/ChartTools/IO/ComponentList.cs index 9da704bc..770d22b8 100644 --- a/ChartTools/IO/ComponentList.cs +++ b/ChartTools/IO/ComponentList.cs @@ -3,27 +3,172 @@ [Flags] public enum DifficultySet : byte { - None = 0, - Easy = 1 << 0, + None = 0, + Easy = 1 << 0, Medium = 1 << 1, - Hard = 1 << 2, + Hard = 1 << 2, Expert = 1 << 3, All = Easy | Medium | Hard | Expert }; -public record ComponentList +public static class DifficultyExtensions { - public bool Metadata { get; set; } - public bool SyncTrack { get; set; } - public bool GlobalEvents { get; set; } - - public DifficultySet Drums { get; set; } - public DifficultySet LeadGuitar { get; set; } - public DifficultySet CoopGuitar { get; set; } - public DifficultySet RythmGuitar { get; set; } - public DifficultySet Bass { get; set; } - public DifficultySet GHLGuitar { get; set; } - public DifficultySet GHLBass { get; set; } - public DifficultySet Keys { get; set; } - public DifficultySet Vocals { get; set; } + public static DifficultySet ToSet(this Difficulty difficulty) => (DifficultySet)(1 << (int)difficulty); +} + +public record ComponentList() +{ + public static readonly ComponentList Full = new() + { + Metadata = true, + SyncTrack = true, + GlobalEvents = true, + Drums = DifficultySet.All, + LeadGuitar = DifficultySet.All, + CoopGuitar = DifficultySet.All, + RythmGuitar = DifficultySet.All, + Bass = DifficultySet.All, + GHLGuitar = DifficultySet.All, + GHLBass = DifficultySet.All, + Keys = DifficultySet.All, + Vocals = DifficultySet.All + }; + + public bool Metadata { get; init; } + public bool SyncTrack { get; init; } + public bool GlobalEvents { get; init; } + + public DifficultySet Drums + { + get => _drums; + init => _drums = value; + } + private DifficultySet _drums; + + public DifficultySet LeadGuitar + { + get => _leadGuitar; + init => _leadGuitar = value; + } + private DifficultySet _leadGuitar; + + public DifficultySet CoopGuitar + { + get => _coopGuitar; + init => _coopGuitar = value; + } + private DifficultySet _coopGuitar; + + public DifficultySet RythmGuitar + { + get => _rythmGuitar; + init => _rythmGuitar = value; + } + private DifficultySet _rythmGuitar; + + public DifficultySet Bass + { + get => _bass; + init => _bass = value; + } + private DifficultySet _bass; + + public DifficultySet GHLGuitar + { + get => _ghlGuitar; + init => _ghlGuitar = value; + } + private DifficultySet _ghlGuitar; + + public DifficultySet GHLBass + { + get => _ghlBass; + init => _ghlBass = value; + } + private DifficultySet _ghlBass; + + public DifficultySet Keys + { + get => _keys; + init => _keys = value; + } + private DifficultySet _keys; + + public DifficultySet Vocals + { + get => _vocals; + init => _vocals = value; + } + private DifficultySet _vocals; + + public ComponentList(InstrumentIdentity instrument) : this() + { + Validator.ValidateEnum(instrument); + MapInstrument(instrument) = DifficultySet.All; + } + + public ComponentList(StandardInstrumentIdentity instrument) : this() + { + Validator.ValidateEnum(instrument); + MapInstrument((InstrumentIdentity)instrument) = DifficultySet.All; + } + + public ComponentList(GHLInstrumentIdentity instrument) : this() + { + Validator.ValidateEnum(instrument); + MapInstrument((InstrumentIdentity)instrument) = DifficultySet.All; + } + + public ComponentList(InstrumentIdentity instrument, Difficulty difficulty) : this() + { + Validator.ValidateEnum(instrument); + Validator.ValidateEnum(difficulty); + + MapInstrument(instrument) = difficulty.ToSet(); + } + + public ComponentList(StandardInstrumentIdentity instrument, Difficulty difficulty) : this() + { + Validator.ValidateEnum(instrument); + Validator.ValidateEnum(difficulty); + + MapInstrument((InstrumentIdentity)instrument) = difficulty.ToSet(); + } + + public ComponentList(GHLInstrumentIdentity instrument, Difficulty difficulty) : this() + { + Validator.ValidateEnum(instrument); + Validator.ValidateEnum(difficulty); + + MapInstrument((InstrumentIdentity)instrument) = difficulty.ToSet(); + } + + public ref readonly DifficultySet GetInstrument(InstrumentIdentity instrument) => ref MapInstrument(instrument); + + private ref DifficultySet MapInstrument(InstrumentIdentity instrument) + { + switch (instrument) + { + case InstrumentIdentity.Drums: + return ref _drums; + case InstrumentIdentity.LeadGuitar: + return ref _leadGuitar; + case InstrumentIdentity.CoopGuitar: + return ref _coopGuitar; + case InstrumentIdentity.RhythmGuitar: + return ref _rythmGuitar; + case InstrumentIdentity.Bass: + return ref _bass; + case InstrumentIdentity.GHLGuitar: + return ref _ghlGuitar; + case InstrumentIdentity.GHLBass: + return ref _ghlBass; + case InstrumentIdentity.Keys; + return ref _keys; + case InstrumentIdentity.Vocals: + return ref _vocals; + default: + throw new UndefinedEnumException(instrument); + } + } } diff --git a/ChartTools/IO/Configuration/Session.cs b/ChartTools/IO/Configuration/Session.cs index 2242dc28..8f2ee7fe 100644 --- a/ChartTools/IO/Configuration/Session.cs +++ b/ChartTools/IO/Configuration/Session.cs @@ -3,10 +3,12 @@ namespace ChartTools.IO.Configuration; -internal abstract class Session(FormattingRules? formatting) +internal abstract class Session(ComponentList components, FormattingRules? formatting) { public abstract ICommonConfiguration Configuration { get; } - public FormattingRules? Formatting { get; set; } = formatting; + public FormattingRules Formatting { get; set; } = formatting ?? new(); + + public ComponentList Components { get; set; } = components; public bool HandleDuplicate(uint position, string objectType, Func checkDuplicate) => Configuration.DuplicateTrackObjectPolicy switch { diff --git a/ChartTools/IO/FileReader.cs b/ChartTools/IO/FileReader.cs index 7dcc5d25..91a7d96d 100644 --- a/ChartTools/IO/FileReader.cs +++ b/ChartTools/IO/FileReader.cs @@ -1,4 +1,5 @@ using ChartTools.Extensions.Collections; +using ChartTools.IO.Configuration; namespace ChartTools.IO; @@ -20,7 +21,7 @@ protected void CheckBusy() public abstract void Dispose(); } -internal abstract class FileReader(string path, Func parserGetter) : FileReader(path) where TParser : FileParser +internal abstract class FileReader(string path) : FileReader(path) where TParser : FileParser { public record ParserContentGroup(TParser Parser, DelayedEnumerableSource Source); @@ -28,7 +29,8 @@ public record ParserContentGroup(TParser Parser, DelayedEnumerableSource Sour protected readonly List parserGroups = []; protected readonly List parseTasks = []; - protected readonly Func parserGetter = parserGetter; + + protected abstract TParser? GetParser(string header); public override void Read() { diff --git a/ChartTools/IO/TextFileReader.cs b/ChartTools/IO/TextFileReader.cs index e75f3659..83efe18a 100644 --- a/ChartTools/IO/TextFileReader.cs +++ b/ChartTools/IO/TextFileReader.cs @@ -3,12 +3,10 @@ namespace ChartTools.IO; -internal abstract class TextFileReader : FileReader +internal abstract class TextFileReader(string path) : FileReader(path) { public virtual bool DefinedSectionEnd { get; } = false; - public TextFileReader(string path, Func parserGetter) : base(path, parserGetter) { } - protected override void ReadBase(bool async, CancellationToken cancellationToken) { ParserContentGroup? currentGroup = null; @@ -28,7 +26,7 @@ protected override void ReadBase(bool async, CancellationToken cancellationToken } var header = enumerator.Current; - var parser = parserGetter(header); + var parser = GetParser(header); if (parser is not null) { From 7ad7b818a4ca004e918a28d8111c23f138c17210 Mon Sep 17 00:00:00 2001 From: TheBoxyBear <15822255+TheBoxyBear@users.noreply.github.com> Date: Mon, 15 Jul 2024 12:40:39 -0400 Subject: [PATCH 4/7] Deprecate ReadTrack --- ChartTools/IO/Chart/ChartFile.cs | 186 +++++++++---------------------- ChartTools/IO/ComponentList.cs | 68 ++++------- ChartTools/Tracks/Track.cs | 52 ++++----- 3 files changed, 103 insertions(+), 203 deletions(-) diff --git a/ChartTools/IO/Chart/ChartFile.cs b/ChartTools/IO/Chart/ChartFile.cs index 4069e62b..d1b3f2be 100644 --- a/ChartTools/IO/Chart/ChartFile.cs +++ b/ChartTools/IO/Chart/ChartFile.cs @@ -62,7 +62,7 @@ private static Song CreateSongFromReader(ChartFileReader reader) /// public static Song ReadSong(string path, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) { - var session = new ChartReadingSession(ComponentList.Full, config, formatting); + var session = new ChartReadingSession(ComponentList.Full(), config, formatting); var reader = new ChartFileReader(path, session); reader.Read(); @@ -71,7 +71,7 @@ public static Song ReadSong(string path, ChartReadingConfiguration? config = def public static async Task ReadSongAsync(string path, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) { - var session = new ChartReadingSession(ComponentList.Full, config, formatting); + var session = new ChartReadingSession(ComponentList.Full(), config, formatting); var reader = new ChartFileReader(path, session); await reader.ReadAsync(cancellationToken); @@ -88,7 +88,7 @@ public static Song ReadComponents(string path, ComponentList components, ChartRe return CreateSongFromReader(reader); } - public static async Task ReadComponents(string path, ComponentList components, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) + public static async Task ReadComponentsAsync(string path, ComponentList components, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) { var session = new ChartReadingSession(components, config, formatting); @@ -122,24 +122,25 @@ public static async Task ReadComponents(string path, ComponentList compone /// Path of the file to read /// Instrument to read /// - public static Instrument? ReadInstrument(string path, InstrumentIdentity instrument, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) + public static Instrument? ReadInstrument(string path, InstrumentIdentity instrument, DifficultySet difficulties = DifficultySet.All, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) { if (instrument == InstrumentIdentity.Drums) - return ReadDrums(path, config, formatting); + return ReadDrums(path, difficulties, config, formatting); if (Enum.IsDefined((GHLInstrumentIdentity)instrument)) - return ReadInstrument(path, (GHLInstrumentIdentity)instrument, config, formatting); + return ReadInstrument(path, (GHLInstrumentIdentity)instrument, difficulties, config, formatting); return Enum.IsDefined((StandardInstrumentIdentity)instrument) - ? ReadInstrument(path, (StandardInstrumentIdentity)instrument, config, formatting) + ? ReadInstrument(path, (StandardInstrumentIdentity)instrument, difficulties, config, formatting) : throw new UndefinedEnumException(instrument); } - public static async Task ReadInstrumentAsync(string path, InstrumentIdentity instrument, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) + + public static async Task ReadInstrumentAsync(string path, InstrumentIdentity instrument, DifficultySet difficulties = DifficultySet.All, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) { if (instrument == InstrumentIdentity.Drums) - return await ReadDrumsAsync(path, config, formatting, cancellationToken); + return await ReadDrumsAsync(path, difficulties, config, formatting, cancellationToken); if (Enum.IsDefined((GHLInstrumentIdentity)instrument)) - return await ReadInstrumentAsync(path, (GHLInstrumentIdentity)instrument, config, formatting, cancellationToken); + return await ReadInstrumentAsync(path, (GHLInstrumentIdentity)instrument, difficulties, config, formatting, cancellationToken); return Enum.IsDefined((StandardInstrumentIdentity)instrument) - ? await ReadInstrumentAsync(path, (StandardInstrumentIdentity)instrument, config, formatting, cancellationToken) + ? await ReadInstrumentAsync(path, (StandardInstrumentIdentity)instrument, difficulties, config, formatting, cancellationToken) : throw new UndefinedEnumException(instrument); } @@ -152,6 +153,7 @@ public static async Task ReadComponents(string path, ComponentList compone /// /// Path of the file to read public static Vocals? ReadVocals(string path) => BuildVocals(ReadGlobalEvents(path)); + public static async Task ReadVocalsAsync(string path, CancellationToken cancellationToken = default) => BuildVocals(await ReadGlobalEventsAsync(path, cancellationToken)); private static Vocals? BuildVocals(IList events) @@ -183,7 +185,7 @@ public static async Task ReadComponents(string path, ComponentList compone /// Path of the file to read /// /// - public static Drums? ReadDrums(string path, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) + public static Drums? ReadDrums(string path, DifficultySet difficulties = DifficultySet.All, ChartReadingConfiguration ? config = default, FormattingRules? formatting = default) { var session = new ChartReadingSession(new(InstrumentIdentity.Drums), config, formatting); var reader = new ChartFileReader(path, session); @@ -191,9 +193,10 @@ public static async Task ReadComponents(string path, ComponentList compone reader.Read(); return CreateInstrumentFromReader(reader); } - public static async Task ReadDrumsAsync(string path, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) + + public static async Task ReadDrumsAsync(string path, DifficultySet difficulties = DifficultySet.All, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) { - var session = new ChartReadingSession(new(InstrumentIdentity.Drums), config, formatting); + var session = new ChartReadingSession(new(InstrumentIdentity.Drums, difficulties), config, formatting); var reader = new ChartFileReader(path, session); await reader.ReadAsync(cancellationToken); @@ -209,17 +212,18 @@ public static async Task ReadComponents(string path, ComponentList compone /// if the file has no data for the given instrument /// /// Path of the file to read - public static GHLInstrument? ReadInstrument(string path, GHLInstrumentIdentity instrument, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) + public static GHLInstrument? ReadInstrument(string path, GHLInstrumentIdentity instrument, DifficultySet difficulties = DifficultySet.All, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) { - var session = new ChartReadingSession(new(instrument), config, formatting); + var session = new ChartReadingSession(new(instrument, difficulties), config, formatting); var reader = new ChartFileReader(path, session); reader.Read(); return CreateInstrumentFromReader(reader); } - public static async Task ReadInstrumentAsync(string path, GHLInstrumentIdentity instrument, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) + + public static async Task ReadInstrumentAsync(string path, GHLInstrumentIdentity instrument, DifficultySet difficulties = DifficultySet.All, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) { - var session = new ChartReadingSession(new(instrument), config, formatting); + var session = new ChartReadingSession(new(instrument, difficulties), config, formatting); var reader = new ChartFileReader(path, session); await reader.ReadAsync(cancellationToken); @@ -228,18 +232,18 @@ public static async Task ReadComponents(string path, ComponentList compone #endregion #region Standard - public static StandardInstrument? ReadInstrument(string path, StandardInstrumentIdentity instrument, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) + public static StandardInstrument? ReadInstrument(string path, StandardInstrumentIdentity instrument, DifficultySet difficulties = DifficultySet.All, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) { - var session = new ChartReadingSession(new(instrument), config, formatting); + var session = new ChartReadingSession(new(instrument, difficulties), config, formatting); var reader = new ChartFileReader(path, session); reader.Read(); return CreateInstrumentFromReader(reader); } - public static async Task ReadInstrumentAsync(string path, StandardInstrumentIdentity instrument, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) + public static async Task ReadInstrumentAsync(string path, StandardInstrumentIdentity instrument, DifficultySet difficulties = DifficultySet.All, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) { - var session = new ChartReadingSession(new(instrument), config, formatting); + var session = new ChartReadingSession(new(instrument, difficulties), config, formatting); var reader = new ChartFileReader(path, session); await reader.ReadAsync(cancellationToken); @@ -249,130 +253,42 @@ public static async Task ReadComponents(string path, ComponentList compone #endregion #region Tracks - /// - /// - /// - /// - /// - public static Track ReadTrack(string path, InstrumentIdentity instrument, Difficulty difficulty, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) - { - if (instrument == InstrumentIdentity.Drums) - return ReadDrumsTrack(path, difficulty, config, formatting); - if (Enum.IsDefined((GHLInstrumentIdentity)instrument)) - return ReadTrack(path, (GHLInstrumentIdentity)instrument, difficulty, config, formatting); - if (Enum.IsDefined((StandardInstrumentIdentity)instrument)) - return ReadTrack(path, (StandardInstrumentIdentity)instrument, difficulty, config, formatting); + [Obsolete($"Use {nameof(ReadInstrument)} with a {nameof(DifficultySet)}.")] + public static Track? ReadTrack(string path, InstrumentIdentity instrument, Difficulty difficulty, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) + => ReadInstrument(path, instrument, difficulty.ToSet(), config, formatting)?.GetTrack(difficulty); - throw new UndefinedEnumException(instrument); - } - /// - /// - /// - /// - /// - /// - public static async Task ReadTrackAsync(string path, InstrumentIdentity instrument, Difficulty difficulty, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) - { - if (instrument == InstrumentIdentity.Drums) - return await ReadDrumsTrackAsync(path, difficulty, config, formatting, cancellationToken); - if (Enum.IsDefined((GHLInstrumentIdentity)instrument)) - return await ReadTrackAsync(path, (GHLInstrumentIdentity)instrument, difficulty, config, formatting, cancellationToken); - if (Enum.IsDefined((StandardInstrumentIdentity)instrument)) - return await ReadTrackAsync(path, (StandardInstrumentIdentity)instrument, difficulty, config, formatting, cancellationToken); + [Obsolete($"Use {nameof(ReadInstrumentAsync)} with a {nameof(DifficultySet)}.")] + public static async Task ReadTrackAsync(string path, InstrumentIdentity instrument, Difficulty difficulty, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) + => (await ReadInstrumentAsync(path, instrument, difficulty.ToSet(), config, formatting, cancellationToken))?.GetTrack(difficulty); - throw new UndefinedEnumException(instrument); - } #region Drums - /// - /// - /// - /// + [Obsolete($"Use {nameof(ReadDrums)} with a {nameof(DifficultySet)}.")] public static Track ReadDrumsTrack(string path, Difficulty difficulty, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) - { - var session = new ChartReadingSession(new(InstrumentIdentity.Drums, difficulty), config, formatting); - var reader = new ChartFileReader(path, session); - - reader.Read(); - return reader.Parsers.TryGetFirstOfType(out DrumsTrackParser? parser) ? parser!.Result! : new(); - } + => ReadDrums(path, difficulty.ToSet(), config, formatting)?.GetTrack(difficulty) ?? new(); - /// - /// - /// - /// - /// + [Obsolete($"Use {nameof(ReadDrumsAsync)} with a {nameof(DifficultySet)}.")] public static async Task> ReadDrumsTrackAsync(string path, Difficulty difficulty, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) - { - var session = new ChartReadingSession(new(InstrumentIdentity.Drums, difficulty), config, formatting); - var reader = new ChartFileReader(path, session); - - await reader.ReadAsync(cancellationToken); - return reader.Parsers.TryGetFirstOfType(out DrumsTrackParser? parser) ? parser!.Result! : new(); - } + => (await ReadDrumsAsync(path, difficulty.ToSet(), config, formatting, cancellationToken))?.GetTrack(difficulty) ?? new(); #endregion #region GHL + [Obsolete($"Use {nameof(ReadInstrument)} with a {nameof(DifficultySet)}.")] + public static Track? ReadTrack(string path, GHLInstrumentIdentity instrument, Difficulty difficulty, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) + => ReadInstrument(path, instrument, difficulty.ToSet(), config, formatting)?.GetTrack(difficulty); - /// - /// - /// - /// - /// - public static Track ReadTrack(string path, GHLInstrumentIdentity instrument, Difficulty difficulty, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) - { - var session = new ChartReadingSession(new(instrument, difficulty), config, formatting); - var reader = new ChartFileReader(path, session); - - reader.Read(); - return reader.Parsers.TryGetFirstOfType(out GHLTrackParser? parser) ? parser!.Result : new(); - } - - /// - /// - /// - /// - /// - /// - public static async Task> ReadTrackAsync(string path, GHLInstrumentIdentity instrument, Difficulty difficulty, ChartReadingConfiguration? config, FormattingRules? formatting = default, CancellationToken cancellationToken = default) - { - var session = new ChartReadingSession(new(instrument, difficulty), config, formatting); - var reader = new ChartFileReader(path, session); - - await reader.ReadAsync(cancellationToken); - return reader.Parsers.TryGetFirstOfType(out GHLTrackParser? parser) ? parser!.Result : new(); - } + [Obsolete($"Use {nameof(ReadInstrumentAsync)} with a {nameof(DifficultySet)}.")] + public static async Task?> ReadTrackAsync(string path, GHLInstrumentIdentity instrument, Difficulty difficulty, ChartReadingConfiguration? config, FormattingRules? formatting = default, CancellationToken cancellationToken = default) + => (await ReadInstrumentAsync(path, instrument, difficulty.ToSet(), config, formatting, cancellationToken))?.GetTrack(difficulty); #endregion #region Standard - /// - /// - /// - /// - /// - public static Track ReadTrack(string path, StandardInstrumentIdentity instrument, Difficulty difficulty, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) - { - var session = new ChartReadingSession(new(instrument, difficulty), config, formatting); - var reader = new ChartFileReader(path, session); - - reader.Read(); - return reader.Parsers.TryGetFirstOfType(out StandardTrackParser? parser) ? parser!.Result! : new(); - } - - /// - /// - /// - /// - /// - /// - /// - public static async Task> ReadTrackAsync(string path, StandardInstrumentIdentity instrument, Difficulty difficulty, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) - { - var session = new ChartReadingSession(new(instrument, difficulty), config, formatting); - var reader = new ChartFileReader(path, session); + [Obsolete($"Use {nameof(ReadInstrument)} with a {nameof(DifficultySet)}.")] + public static Track? ReadTrack(string path, StandardInstrumentIdentity instrument, Difficulty difficulty, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) + => ReadInstrument(path, instrument, difficulty.ToSet(), config, formatting)?.GetTrack(difficulty); - await reader.ReadAsync(cancellationToken); - return reader.Parsers.TryGetFirstOfType(out StandardTrackParser? parser) ? parser!.Result! : new(); - } + [Obsolete($"Use {nameof(ReadInstrumentAsync)} with a {nameof(DifficultySet)}.")] + public static async Task?> ReadTrackAsync(string path, StandardInstrumentIdentity instrument, Difficulty difficulty, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) + => (await ReadInstrumentAsync(path, instrument, difficulty.ToSet(), config, formatting, cancellationToken))?.GetTrack(difficulty); #endregion #endregion @@ -423,6 +339,7 @@ public static async Task> ReadGlobalEventsAsync(string path, C /// Path of the file to read /// public static IEnumerable ReadLyrics(string path) => ReadGlobalEvents(path).GetLyrics(); + /// /// Reads lyrics from a chart file asynchronously using multitasking. /// @@ -588,11 +505,13 @@ public static void ReplaceGlobalEvents(string path, IEnumerable eve var writer = GetGlobalEventWriter(path, events, new(DefaultWriteConfig, null)); writer.Write(); } + public static async Task ReplaceGlobalEventsAsync(string path, IEnumerable events, CancellationToken cancellationToken = default) { var writer = GetGlobalEventWriter(path, events, new(DefaultWriteConfig, null)); await writer.WriteAsync(cancellationToken); } + private static ChartFileWriter GetGlobalEventWriter(string path, IEnumerable events, ChartWritingSession session) => new(path, null, new GlobalEventSerializer(events, session)); /// @@ -606,12 +525,13 @@ public static void ReplaceSyncTrack(string path, SyncTrack syncTrack, ChartWriti var writer = GetSyncTrackWriter(path, syncTrack, new(config, null)); writer.Write(); } - /// + public static async Task ReplaceSyncTrackAsync(string path, SyncTrack syncTrack, ChartWritingConfiguration? config = default, CancellationToken cancellationToken = default) { var writer = GetSyncTrackWriter(path, syncTrack, new(config, null)); await writer.WriteAsync(cancellationToken); } + private static ChartFileWriter GetSyncTrackWriter(string path, SyncTrack syncTrack, ChartWritingSession session) => new(path, null, new SyncTrackSerializer(syncTrack, session)); #endregion diff --git a/ChartTools/IO/ComponentList.cs b/ChartTools/IO/ComponentList.cs index 770d22b8..e7be8df5 100644 --- a/ChartTools/IO/ComponentList.cs +++ b/ChartTools/IO/ComponentList.cs @@ -18,7 +18,7 @@ public static class DifficultyExtensions public record ComponentList() { - public static readonly ComponentList Full = new() + public static ComponentList Full() => new() { Metadata = true, SyncTrack = true, @@ -34,125 +34,105 @@ public record ComponentList() Vocals = DifficultySet.All }; - public bool Metadata { get; init; } - public bool SyncTrack { get; init; } - public bool GlobalEvents { get; init; } + public bool Metadata { get; set; } + public bool SyncTrack { get; set; } + public bool GlobalEvents { get; set; } public DifficultySet Drums { get => _drums; - init => _drums = value; + set => _drums = value; } private DifficultySet _drums; public DifficultySet LeadGuitar { get => _leadGuitar; - init => _leadGuitar = value; + set => _leadGuitar = value; } private DifficultySet _leadGuitar; public DifficultySet CoopGuitar { get => _coopGuitar; - init => _coopGuitar = value; + set => _coopGuitar = value; } private DifficultySet _coopGuitar; public DifficultySet RythmGuitar { get => _rythmGuitar; - init => _rythmGuitar = value; + set => _rythmGuitar = value; } private DifficultySet _rythmGuitar; public DifficultySet Bass { get => _bass; - init => _bass = value; + set => _bass = value; } private DifficultySet _bass; public DifficultySet GHLGuitar { get => _ghlGuitar; - init => _ghlGuitar = value; + set => _ghlGuitar = value; } private DifficultySet _ghlGuitar; public DifficultySet GHLBass { get => _ghlBass; - init => _ghlBass = value; + set => _ghlBass = value; } private DifficultySet _ghlBass; public DifficultySet Keys { get => _keys; - init => _keys = value; + set => _keys = value; } private DifficultySet _keys; public DifficultySet Vocals { get => _vocals; - init => _vocals = value; + set => _vocals = value; } private DifficultySet _vocals; - public ComponentList(InstrumentIdentity instrument) : this() + public ComponentList(InstrumentIdentity instrument, DifficultySet difficulties = DifficultySet.All) : this() { Validator.ValidateEnum(instrument); - MapInstrument(instrument) = DifficultySet.All; - } + Validator.ValidateEnum(difficulties); - public ComponentList(StandardInstrumentIdentity instrument) : this() - { - Validator.ValidateEnum(instrument); - MapInstrument((InstrumentIdentity)instrument) = DifficultySet.All; - } - - public ComponentList(GHLInstrumentIdentity instrument) : this() - { - Validator.ValidateEnum(instrument); - MapInstrument((InstrumentIdentity)instrument) = DifficultySet.All; + MapInstrument(instrument) = difficulties; } - public ComponentList(InstrumentIdentity instrument, Difficulty difficulty) : this() + public ComponentList(StandardInstrumentIdentity instrument, DifficultySet difficulties = DifficultySet.All) : this() { Validator.ValidateEnum(instrument); - Validator.ValidateEnum(difficulty); + Validator.ValidateEnum(difficulties); - MapInstrument(instrument) = difficulty.ToSet(); + MapInstrument((InstrumentIdentity)instrument) = difficulties; } - public ComponentList(StandardInstrumentIdentity instrument, Difficulty difficulty) : this() + public ComponentList(GHLInstrumentIdentity instrument, DifficultySet difficulties = DifficultySet.All) : this() { Validator.ValidateEnum(instrument); - Validator.ValidateEnum(difficulty); + Validator.ValidateEnum(difficulties); - MapInstrument((InstrumentIdentity)instrument) = difficulty.ToSet(); + MapInstrument((InstrumentIdentity)instrument) = difficulties; } - public ComponentList(GHLInstrumentIdentity instrument, Difficulty difficulty) : this() - { - Validator.ValidateEnum(instrument); - Validator.ValidateEnum(difficulty); - - MapInstrument((InstrumentIdentity)instrument) = difficulty.ToSet(); - } - - public ref readonly DifficultySet GetInstrument(InstrumentIdentity instrument) => ref MapInstrument(instrument); - - private ref DifficultySet MapInstrument(InstrumentIdentity instrument) + public ref DifficultySet MapInstrument(InstrumentIdentity instrument) { switch (instrument) { case InstrumentIdentity.Drums: return ref _drums; case InstrumentIdentity.LeadGuitar: - return ref _leadGuitar; + return ref _leadGuitar;2 case InstrumentIdentity.CoopGuitar: return ref _coopGuitar; case InstrumentIdentity.RhythmGuitar: diff --git a/ChartTools/Tracks/Track.cs b/ChartTools/Tracks/Track.cs index c0c6acef..32fde3c4 100644 --- a/ChartTools/Tracks/Track.cs +++ b/ChartTools/Tracks/Track.cs @@ -71,63 +71,63 @@ internal IEnumerable SoloToStarPower(bool removeEvents) #region File reading #region Single file - [Obsolete($"Use {nameof(ChartFile.ReadTrack)}.")] - public static Track FromFile(string path, InstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default) => ExtensionHandler.Read(path, (".chart", path => ChartFile.ReadTrack(path, instrument, difficulty, config?.Chart, formatting))); + [Obsolete($"Use {nameof(ChartFile.ReadInstrument)} with a difficulty set.")] + public static Track FromFile(string path, InstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default) => ExtensionHandler.Read(path, (".chart", path => ChartFile.ReadTrack(path, instrument, difficulty, config?.Chart, formatting))); - [Obsolete($"Use {nameof(ChartFile.ReadTrackAsync)}.")] - public static async Task FromFileAsync(string path, InstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) => await ExtensionHandler.ReadAsync(path, (".chart", path => ChartFile.ReadTrackAsync(path, instrument, difficulty, config?.Chart, formatting, cancellationToken))); + [Obsolete($"Use {nameof(ChartFile.ReadInstrumentAsync)} with a difficulty set.")] + public static async Task FromFileAsync(string path, InstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) => await ExtensionHandler.ReadAsync(path, (".chart", path => ChartFile.ReadTrackAsync(path, instrument, difficulty, config?.Chart, formatting, cancellationToken))); - [Obsolete($"Use {nameof(ChartFile.ReadDrumsTrack)}.")] - public static Track FromFile(string path, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default) => ExtensionHandler.Read>(path, (".chart", path => ChartFile.ReadDrumsTrack(path, difficulty, config?.Chart, formatting))); + [Obsolete($"Use {nameof(ChartFile.ReadDrums)} with a difficulty set.")] + public static Track FromFile(string path, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default) => ExtensionHandler.Read(path, (".chart", path => ChartFile.ReadDrumsTrack(path, difficulty, config?.Chart, formatting))); - [Obsolete($"Use {nameof(ChartFile.ReadDrumsTrackAsync)}.")] - public static async Task> FromFileAsync(string path, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) => await ExtensionHandler.ReadAsync>(path, (".chart", path => ChartFile.ReadDrumsTrackAsync(path, difficulty, config?.Chart, formatting, cancellationToken))); + [Obsolete($"Use {nameof(ChartFile.ReadDrumsAsync)} with a difficulty set.")] + public static async Task> FromFileAsync(string path, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) => await ExtensionHandler.ReadAsync(path, (".chart", path => ChartFile.ReadDrumsTrackAsync(path, difficulty, config?.Chart, formatting, cancellationToken))); - [Obsolete($"Use {nameof(ChartFile.ReadTrack)}.")] - public static Track FromFile(string path, GHLInstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default) => ExtensionHandler.Read>(path, (".chart", path => ChartFile.ReadTrack(path, instrument, difficulty, config?.Chart, formatting))); + [Obsolete($"Use {nameof(ChartFile.ReadInstrument)} with a difficulty set.")] + public static Track FromFile(string path, GHLInstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default) => ExtensionHandler.Read(path, (".chart", path => ChartFile.ReadTrack(path, instrument, difficulty, config?.Chart, formatting))); - [Obsolete($"Use {nameof(ChartFile.ReadTrackAsync)}.")] - public static async Task> FromFileAsync(string path, GHLInstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) => await ExtensionHandler.ReadAsync>(path, (".chart", path => ChartFile.ReadTrackAsync(path, instrument, difficulty, config?.Chart, formatting, cancellationToken))); + [Obsolete($"Use {nameof(ChartFile.ReadInstrumentAsync)} with a difficulty set.")] + public static async Task> FromFileAsync(string path, GHLInstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) => await ExtensionHandler.ReadAsync(path, (".chart", path => ChartFile.ReadTrackAsync(path, instrument, difficulty, config?.Chart, formatting, cancellationToken))); - [Obsolete($"Use {nameof(ChartFile.ReadTrack)}.")] - public static Track FromFile(string path, StandardInstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default) => ExtensionHandler.Read>(path, (".chart", path => ChartFile.ReadTrack(path, instrument, difficulty, config?.Chart, formatting))); + [Obsolete($"Use {nameof(ChartFile.ReadInstrument)} with a difficulty set.")] + public static Track FromFile(string path, StandardInstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default) => ExtensionHandler.Read(path, (".chart", path => ChartFile.ReadTrack(path, instrument, difficulty, config?.Chart, formatting))); - [Obsolete($"Use {nameof(ChartFile.ReadTrackAsync)}.")] - public static async Task> FromFileAsync(string path, StandardInstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) => await ExtensionHandler.ReadAsync>(path, (".chart", path => ChartFile.ReadTrackAsync(path, instrument, difficulty, config?.Chart, formatting, cancellationToken))); + [Obsolete($"Use {nameof(ChartFile.ReadInstrumentAsync)} with a difficulty set.")] + public static async Task> FromFileAsync(string path, StandardInstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) => await ExtensionHandler.ReadAsync(path, (".chart", path => ChartFile.ReadTrackAsync(path, instrument, difficulty, config?.Chart, formatting, cancellationToken))); #endregion #region Directory - [Obsolete($"Use {nameof(ChartFile.ReadTrack)} with {nameof(Metadata.Formatting)}.")] + [Obsolete($"Use {nameof(ChartFile.ReadInstrument)} with a {nameof(DifficultySet)} and {nameof(Metadata.Formatting)}.")] public static DirectoryResult FromDirectory(string directory, InstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default) => DirectoryHandler.FromDirectory(directory, (path, formatting) => FromFile(path, instrument, difficulty, config, formatting)); - [Obsolete($"Use {nameof(ChartFile.ReadTrackAsync)} with {nameof(Metadata.Formatting)}.")] + [Obsolete($"Use {nameof(ChartFile.ReadInstrumentAsync)} with a {nameof(DifficultySet)} and {nameof(Metadata.Formatting)}.")] public static async Task> FromDirectoryAsync(string directory, InstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, CancellationToken cancellationToken = default) => await DirectoryHandler.FromDirectoryAsync(directory, async (path, formatting) => await FromFileAsync(path, instrument, difficulty, config, formatting, cancellationToken), cancellationToken); - [Obsolete($"Use {nameof(ChartFile.ReadDrumsTrack)} with {nameof(Metadata.Formatting)}.")] + [Obsolete($"Use {nameof(ChartFile.ReadDrums)} with a {nameof(DifficultySet)} and {nameof(Metadata.Formatting)}.")] public static DirectoryResult?> FromDirectory(string directory, Difficulty difficulty, ReadingConfiguration? config = default) => DirectoryHandler.FromDirectory(directory, (path, formatting) => FromFile(path, difficulty, config, formatting)); - [Obsolete($"Use {nameof(ChartFile.ReadDrumsTrackAsync)} with {nameof(Metadata.Formatting)}.")] + [Obsolete($"Use {nameof(ChartFile.ReadDrumsAsync)} with a {nameof(DifficultySet)} and {nameof(Metadata.Formatting)}.")] public static async Task?>> FromDirectoryAsync(string directory, Difficulty difficulty, ReadingConfiguration? config = default, CancellationToken cancellationToken = default) => await DirectoryHandler.FromDirectoryAsync(directory, async (path, formatting) => await FromFileAsync(path, difficulty, config, formatting, cancellationToken), cancellationToken); - [Obsolete($"Use {nameof(ChartFile.ReadTrack)} with {nameof(Metadata.Formatting)}.")] + [Obsolete($"Use {nameof(ChartFile.ReadInstrument)} with a {nameof(DifficultySet)} and {nameof(Metadata.Formatting)}.")] public static DirectoryResult?> FromDirectory(string directory, GHLInstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default) => DirectoryHandler.FromDirectory(directory, (path, formatting) => FromFile(path, instrument, difficulty, config, formatting)); - [Obsolete($"Use {nameof(ChartFile.ReadTrackAsync)} with {nameof(Metadata.Formatting)}.")] + [Obsolete($"Use {nameof(ChartFile.ReadInstrumentAsync)} with a {nameof(DifficultySet)} and {nameof(Metadata.Formatting)}.")] public static async Task?>> FromDirectoryAsync(string directory, GHLInstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, CancellationToken cancellationToken = default) => await DirectoryHandler.FromDirectoryAsync(directory, async (path, formatting) => await FromFileAsync(path, instrument, difficulty, config, formatting, cancellationToken), cancellationToken); - [Obsolete($"Use {nameof(ChartFile.ReadTrack)} with {nameof(Metadata.Formatting)}.")] + [Obsolete($"Use {nameof(ChartFile.ReadInstrument)} with a {nameof(DifficultySet)} and {nameof(Metadata.Formatting)}.")] public static DirectoryResult?> FromDirectory(string directory, StandardInstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default) => DirectoryHandler.FromDirectory(directory, (path, formatting) => FromFile(path, instrument, difficulty, config, formatting)); - [Obsolete($"Use {nameof(ChartFile.ReadTrackAsync)} with {nameof(Metadata.Formatting)}.")] + [Obsolete($"Use {nameof(ChartFile.ReadInstrumentAsync)} with a {nameof(DifficultySet)} and {nameof(Metadata.Formatting)}.")] public static async Task?>> FromDirectoryAsync(string directory, StandardInstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, CancellationToken cancellationToken = default) => await DirectoryHandler.FromDirectoryAsync(directory, async (path, formatting) => await FromFileAsync(path, instrument, difficulty, config, formatting, cancellationToken), cancellationToken); #endregion #endregion [Obsolete($"Use {nameof(ChartFile.ReplaceTrack)}.")] - public void ToFile(string path, WritingConfiguration? config = default, FormattingRules? formatting = default) => ExtensionHandler.Write(path, this, (".chart", (path, track) => ChartFile.ReplaceTrack(path, track, config?.Chart, formatting))); + public void ToFile(string path, WritingConfiguration? config = default, FormattingRules? formatting = default) => ExtensionHandler.Write(path, this, (".chart", (path, track) => ChartFile.ReplaceTrack(path, track, config?.Chart, formatting))); [Obsolete($"Use {nameof(ChartFile.ReplaceTrackAsync)}.")] - public async Task ToFileAsync(string path, WritingConfiguration? config = default,FormattingRules? formatting = default, CancellationToken cancellationToken = default) => await ExtensionHandler.WriteAsync(path, this, (".chart", (path, track) => ChartFile.ReplaceTrackAsync(path, track, config?.Chart, formatting, cancellationToken))); + public async Task ToFileAsync(string path, WritingConfiguration? config = default,FormattingRules? formatting = default, CancellationToken cancellationToken = default) => await ExtensionHandler.WriteAsync(path, this, (".chart", (path, track) => ChartFile.ReplaceTrackAsync(path, track, config?.Chart, formatting, cancellationToken))); public override string ToString() => Difficulty.ToString(); } From f054a4633ab42a5aa95685d2fbf596488665697d Mon Sep 17 00:00:00 2001 From: TheBoxyBear <15822255+TheBoxyBear@users.noreply.github.com> Date: Mon, 15 Jul 2024 15:44:36 -0400 Subject: [PATCH 5/7] #71 Implement component list --- ChartTools/IO/Chart/ChartFile.cs | 163 ++++++++++-------- ChartTools/IO/Chart/ChartFileReader.cs | 7 +- .../Sessions/ChartReadingSession.cs | 7 +- .../Configuration/Sessions/ChartSession.cs | 5 +- .../Chart/Serializing/SyncTrackSerializer.cs | 2 +- ChartTools/IO/Components/ComponentList.cs | 25 +++ .../InstrumentComponentList.cs} | 76 ++++---- ChartTools/IO/Configuration/Session.cs | 4 +- ChartTools/IO/Ini/IniFile.cs | 4 +- ChartTools/IO/Ini/IniFileReader.cs | 8 +- ChartTools/Instruments/Instrument.cs | 2 +- ChartTools/Tracks/Track.cs | 1 + 12 files changed, 181 insertions(+), 123 deletions(-) create mode 100644 ChartTools/IO/Components/ComponentList.cs rename ChartTools/IO/{ComponentList.cs => Components/InstrumentComponentList.cs} (63%) diff --git a/ChartTools/IO/Chart/ChartFile.cs b/ChartTools/IO/Chart/ChartFile.cs index d1b3f2be..f89ebf10 100644 --- a/ChartTools/IO/Chart/ChartFile.cs +++ b/ChartTools/IO/Chart/ChartFile.cs @@ -5,6 +5,7 @@ using ChartTools.IO.Chart.Configuration.Sessions; using ChartTools.IO.Chart.Parsing; using ChartTools.IO.Chart.Serializing; +using ChartTools.IO.Components; using ChartTools.IO.Configuration; using ChartTools.IO.Formatting; using ChartTools.Lyrics; @@ -187,7 +188,7 @@ public static async Task ReadComponentsAsync(string path, ComponentList co /// public static Drums? ReadDrums(string path, DifficultySet difficulties = DifficultySet.All, ChartReadingConfiguration ? config = default, FormattingRules? formatting = default) { - var session = new ChartReadingSession(new(InstrumentIdentity.Drums), config, formatting); + var session = new ChartReadingSession(new() { Instruments = new(InstrumentIdentity.Drums, difficulties) }, config, formatting); var reader = new ChartFileReader(path, session); reader.Read(); @@ -196,7 +197,7 @@ public static async Task ReadComponentsAsync(string path, ComponentList co public static async Task ReadDrumsAsync(string path, DifficultySet difficulties = DifficultySet.All, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) { - var session = new ChartReadingSession(new(InstrumentIdentity.Drums, difficulties), config, formatting); + var session = new ChartReadingSession(new() { Instruments = new(InstrumentIdentity.Drums) }, config, formatting); var reader = new ChartFileReader(path, session); await reader.ReadAsync(cancellationToken); @@ -214,7 +215,7 @@ public static async Task ReadComponentsAsync(string path, ComponentList co /// Path of the file to read public static GHLInstrument? ReadInstrument(string path, GHLInstrumentIdentity instrument, DifficultySet difficulties = DifficultySet.All, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) { - var session = new ChartReadingSession(new(instrument, difficulties), config, formatting); + var session = new ChartReadingSession(new() { Instruments = new(instrument, difficulties) }, config, formatting); var reader = new ChartFileReader(path, session); reader.Read(); @@ -223,7 +224,7 @@ public static async Task ReadComponentsAsync(string path, ComponentList co public static async Task ReadInstrumentAsync(string path, GHLInstrumentIdentity instrument, DifficultySet difficulties = DifficultySet.All, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) { - var session = new ChartReadingSession(new(instrument, difficulties), config, formatting); + var session = new ChartReadingSession(new() { Instruments = new(instrument, difficulties) }, config, formatting); var reader = new ChartFileReader(path, session); await reader.ReadAsync(cancellationToken); @@ -234,7 +235,7 @@ public static async Task ReadComponentsAsync(string path, ComponentList co #region Standard public static StandardInstrument? ReadInstrument(string path, StandardInstrumentIdentity instrument, DifficultySet difficulties = DifficultySet.All, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) { - var session = new ChartReadingSession(new(instrument, difficulties), config, formatting); + var session = new ChartReadingSession(new() { Instruments = new(instrument, difficulties) }, config, formatting); var reader = new ChartFileReader(path, session); reader.Read(); @@ -243,7 +244,7 @@ public static async Task ReadComponentsAsync(string path, ComponentList co public static async Task ReadInstrumentAsync(string path, StandardInstrumentIdentity instrument, DifficultySet difficulties = DifficultySet.All, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) { - var session = new ChartReadingSession(new(instrument, difficulties), config, formatting); + var session = new ChartReadingSession(new() { Instruments = new(instrument, difficulties) }, config, formatting); var reader = new ChartFileReader(path, session); await reader.ReadAsync(cancellationToken); @@ -349,7 +350,6 @@ public static async Task> ReadGlobalEventsAsync(string path, C #endregion #region Sync track - /// /// /// @@ -378,6 +378,54 @@ public static async Task ReadSyncTrackAsync(string path, ChartReading #endregion #region Writing + private static void FillInstrumentWriterData(Instrument? inst, InstrumentIdentity identity, DifficultySet tracks, ChartWritingSession session, + ICollection> serializers, ICollection removedHeaders) + { + // Only act on tracks specified in the component list + foreach (var diff in EnumCache.Values.Where(d => tracks.HasFlag(d.ToSet()))) + { + var track = inst?.GetTrack(diff); + + if (track?.IsEmpty is not null or false) + serializers.Add(new TrackSerializer(track, session)); + else // No track data for the instrument and difficulty + removedHeaders.Add(ChartFormatting.Header(identity, diff)); + } + } + + private static ChartFileWriter GetSongWriter(string path, Song song, ComponentList components, ChartWritingSession session) + { + var removedHeaders = new List(); + var serializers = new List>(); + + if (components.Metadata) + serializers.Add(new MetadataSerializer(song.Metadata)); + + if (components.SyncTrack) + { + if (!song.SyncTrack.IsEmpty) + serializers.Add(new SyncTrackSerializer(song.SyncTrack, session)); + else + removedHeaders.Add(ChartFormatting.SyncTrackHeader); + } + + if (components.GlobalEvents) + { + if (song.GlobalEvents.Count > 0) + serializers.Add(new GlobalEventSerializer(song.GlobalEvents, session)); + else + removedHeaders.Add(ChartFormatting.GlobalEventHeader); + } + + foreach (var identity in EnumCache.Values) + FillInstrumentWriterData(song.Instruments.Get(identity), identity, components.Instruments.Map(identity), session, serializers, removedHeaders); + + if (song.UnknownChartSections is not null) + serializers.AddRange(song.UnknownChartSections.Select(s => new UnknownSectionSerializer(s.Header, s, session))); + + return new(path, removedHeaders, [.. serializers]); + } + /// /// Writes a song to a chart file. /// @@ -385,55 +433,36 @@ public static async Task ReadSyncTrackAsync(string path, ChartReading /// Song to write public static void WriteSong(string path, Song song, ChartWritingConfiguration? config = default) { - var writer = GetSongWriter(path, song, new(config, song.Metadata.Formatting)); + var writer = GetSongWriter(path, song, ComponentList.Full(), new(config, song.Metadata.Formatting)); writer.Write(); } + public static async Task WriteSongAsync(string path, Song song, ChartWritingConfiguration? config = default, CancellationToken cancellationToken = default) { - var writer = GetSongWriter(path, song, new(config, song.Metadata.Formatting)); + var writer = GetSongWriter(path, song, ComponentList.Full(), new(config, song.Metadata.Formatting)); await writer.WriteAsync(cancellationToken); } - private static ChartFileWriter GetSongWriter(string path, Song song, ChartWritingSession session) - { - var instruments = song.Instruments.NonNull().ToArray(); - var serializers = new List>(instruments.Length + 2); - var removedHeaders = new List(); - - serializers.Add(new MetadataSerializer(song.Metadata)); - if (!song.SyncTrack.IsEmpty) - serializers.Add(new SyncTrackSerializer(song.SyncTrack, session)); - else - removedHeaders.Add(ChartFormatting.SyncTrackHeader); - - if (song.GlobalEvents.Count > 0) - serializers.Add(new GlobalEventSerializer(song.GlobalEvents, session)); - else - removedHeaders.Add(ChartFormatting.GlobalEventHeader); - - var difficulties = EnumCache.Values; - - // Remove headers for null instruments - removedHeaders.AddRange((from identity in Enum.GetValues() - where instruments.Any(instrument => instrument.InstrumentIdentity == identity) - let instrumentName = ChartFormatting.InstrumentHeaderNames[identity] - let headers = from diff in difficulties - select ChartFormatting.Header(identity, diff) - select headers).SelectMany(h => h)); + public static void ReplaceComponents(string path, Song song, ComponentList components, ChartWritingConfiguration? config = default) + { + var writer = GetSongWriter(path, song, components, new(config, song.Metadata.Formatting)); + writer.Write(); + } - foreach (var instrument in instruments) - { - var instrumentName = ChartFormatting.InstrumentHeaderNames[instrument.InstrumentIdentity]; - var tracks = instrument.GetExistingTracks().ToArray(); + public static async Task ReplaceComponentsAsync(string path, Song song, ComponentList components, ChartWritingConfiguration? config = default, CancellationToken cancellationToken = default) + { + var writer = GetSongWriter(path, song, components, new(config, song.Metadata.Formatting)); + await writer.WriteAsync(cancellationToken); + } - serializers.AddRange(tracks.Select(t => new TrackSerializer(t, session))); - removedHeaders.AddRange(difficulties.Where(diff => !tracks.Any(t => t.Difficulty == diff)).Select(diff => ChartFormatting.Header(instrumentName, diff))); - } + private static ChartFileWriter GetInstrumentWriter(string path, Instrument? instrument, InstrumentIdentity identity, ChartWritingSession session) + { + var removedHeaders = new List(); + var serializers = new List>(); - if (song.UnknownChartSections is not null) - serializers.AddRange(song.UnknownChartSections.Select(s => new UnknownSectionSerializer(s.Header, s, session))); + FillInstrumentWriterData(instrument, identity, DifficultySet.All, session, serializers, removedHeaders); - return new(path, removedHeaders, serializers.ToArray()); + return new(path, removedHeaders, [.. serializers]); } /// @@ -442,46 +471,39 @@ select ChartFormatting.Header(identity, diff) /// Path of the file to write public static void ReplaceInstrument(string path, Instrument instrument, ChartWritingConfiguration? config = default, FormattingRules? formatting = default) { - var writer = GetInstrumentWriter(path, instrument, new(config, formatting ?? new())); + var writer = GetInstrumentWriter(path, instrument, instrument.InstrumentIdentity, new(config, formatting)); writer.Write(); } + public static async Task ReplaceInstrumentAsync(string path, Instrument instrument, ChartWritingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) { - var writer = GetInstrumentWriter(path, instrument, new(config, formatting ?? new())); + var writer = GetInstrumentWriter(path, instrument, instrument.InstrumentIdentity, new(config, formatting)); await writer.WriteAsync(cancellationToken); } - private static ChartFileWriter GetInstrumentWriter(string path, Instrument instrument, ChartWritingSession session) - { - if (!Enum.IsDefined(instrument.InstrumentIdentity)) - throw new ArgumentException("Instrument cannot be written because its identity is unknown.", nameof(instrument)); - var instrumentName = ChartFormatting.InstrumentHeaderNames[instrument.InstrumentIdentity]; - var tracks = instrument.GetExistingTracks().ToArray(); + private static ChartFileWriter GetTrackWriter(string path, Track track, ChartWritingSession session) + { + if (track.ParentInstrument is null) + throw new ArgumentNullException(nameof(track), "Cannot write track because it does not belong to an instrument."); + if (!Enum.IsDefined(track.ParentInstrument.InstrumentIdentity)) + throw new ArgumentException("Cannot write track because the instrument it belongs to is unknown.", nameof(track)); - return new(path, - EnumCache.Values.Where(d => !tracks.Any(t => t.Difficulty == d)).Select(d => ChartFormatting.Header(instrumentName, d)), - tracks.Select(t => new TrackSerializer(t, session)).ToArray()); + return new(path, null, new TrackSerializer(track, session)); } public static void ReplaceTrack(string path, Track track, ChartWritingConfiguration? config = default, FormattingRules? formatting = default) { - var writer = GetTrackWriter(path, track, new(config, formatting ?? new())); + var writer = GetTrackWriter(path, track, new(config, formatting)); writer.Write(); } + public static async Task ReplaceTrackAsync(string path, Track track, ChartWritingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) { - var writer = GetTrackWriter(path, track, new(config, formatting ?? new())); + var writer = GetTrackWriter(path, track, new(config, formatting)); await writer.WriteAsync(cancellationToken); } - private static ChartFileWriter GetTrackWriter(string path, Track track, ChartWritingSession session) - { - if (track.ParentInstrument is null) - throw new ArgumentNullException(nameof(track), "Cannot write track because it does not belong to an instrument."); - if (!Enum.IsDefined(track.ParentInstrument.InstrumentIdentity)) - throw new ArgumentException("Cannot write track because the instrument it belongs to is unknown.", nameof(track)); - return new(path, null, new TrackSerializer(track, session)); - } + private static ChartFileWriter GetMetadataWriter(string path, Metadata metadata) => new(path, null, new MetadataSerializer(metadata)); /// /// Replaces the metadata in a file. @@ -493,7 +515,8 @@ public static void ReplaceMetadata(string path, Metadata metadata) var writer = GetMetadataWriter(path, metadata); writer.Write(); } - private static ChartFileWriter GetMetadataWriter(string path, Metadata metadata) => new(path, null, new MetadataSerializer(metadata)); + + private static ChartFileWriter GetGlobalEventWriter(string path, IEnumerable events, ChartWritingSession session) => new(path, null, new GlobalEventSerializer(events, session)); /// /// Replaces the global events in a file. @@ -512,7 +535,7 @@ public static async Task ReplaceGlobalEventsAsync(string path, IEnumerable events, ChartWritingSession session) => new(path, null, new GlobalEventSerializer(events, session)); + private static ChartFileWriter GetSyncTrackWriter(string path, SyncTrack syncTrack, ChartWritingSession session) => new(path, null, new SyncTrackSerializer(syncTrack, session)); /// /// Replaces the sync track in a file. @@ -522,7 +545,7 @@ public static async Task ReplaceGlobalEventsAsync(string path, IEnumerable public static void ReplaceSyncTrack(string path, SyncTrack syncTrack, ChartWritingConfiguration? config = default) { - var writer = GetSyncTrackWriter(path, syncTrack, new(config, null)); + var writer = GetSyncTrackWriter(path, syncTrack, new( config, null)); writer.Write(); } @@ -531,8 +554,6 @@ public static async Task ReplaceSyncTrackAsync(string path, SyncTrack syncTrack, var writer = GetSyncTrackWriter(path, syncTrack, new(config, null)); await writer.WriteAsync(cancellationToken); } - - private static ChartFileWriter GetSyncTrackWriter(string path, SyncTrack syncTrack, ChartWritingSession session) => new(path, null, new SyncTrackSerializer(syncTrack, session)); #endregion /// diff --git a/ChartTools/IO/Chart/ChartFileReader.cs b/ChartTools/IO/Chart/ChartFileReader.cs index 336d1985..f09cc194 100644 --- a/ChartTools/IO/Chart/ChartFileReader.cs +++ b/ChartTools/IO/Chart/ChartFileReader.cs @@ -1,5 +1,6 @@ using ChartTools.IO.Chart.Configuration.Sessions; using ChartTools.IO.Chart.Parsing; +using ChartTools.IO.Components; using ChartTools.IO.Configuration; using ChartTools.IO.Parsing; @@ -27,13 +28,13 @@ internal class ChartFileReader(string path, ChartReadingSession session) : TextF return session.Components.SyncTrack ? new SyncTrackParser(session) : null; default: if (ChartFormatting.DrumsTrackHeaders.TryGetValue(header, out Difficulty diff)) - return session.Components.Drums.HasFlag(diff.ToSet()) + return session.Components.Instruments.Drums.HasFlag(diff.ToSet()) ? new DrumsTrackParser(diff, session, header) : null; else if (ChartFormatting.GHLTrackHeaders.TryGetValue(header, out (Difficulty, GHLInstrumentIdentity) ghlTuple)) - return session.Components.Drums.HasFlag(ghlTuple.Item1.ToSet()) + return session.Components.Instruments.Map(ghlTuple.Item2).HasFlag(ghlTuple.Item1.ToSet()) ? new GHLTrackParser(ghlTuple.Item1, ghlTuple.Item2, session, header) : null; else if (ChartFormatting.StandardTrackHeaders.TryGetValue(header, out (Difficulty, StandardInstrumentIdentity) standardTuple)) - return session.Components.Drums.HasFlag(standardTuple.Item1.ToSet()) + return session.Components.Instruments.Map(standardTuple.Item2).HasFlag(standardTuple.Item1.ToSet()) ? new StandardTrackParser(standardTuple.Item1, standardTuple.Item2, session, header) : null; else { diff --git a/ChartTools/IO/Chart/Configuration/Sessions/ChartReadingSession.cs b/ChartTools/IO/Chart/Configuration/Sessions/ChartReadingSession.cs index 96a9cddf..dc5292da 100644 --- a/ChartTools/IO/Chart/Configuration/Sessions/ChartReadingSession.cs +++ b/ChartTools/IO/Chart/Configuration/Sessions/ChartReadingSession.cs @@ -1,11 +1,14 @@ -using ChartTools.IO.Configuration; +using ChartTools.IO.Components; +using ChartTools.IO.Configuration; using ChartTools.IO.Formatting; namespace ChartTools.IO.Chart.Configuration.Sessions; internal class ChartReadingSession(ComponentList components, ChartReadingConfiguration? config, FormattingRules? formatting) - : ChartSession(components, formatting) + : ChartSession(formatting) { + public ComponentList Components { get; set; } = components; + public override ChartReadingConfiguration Configuration { get; } = config ?? ChartFile.DefaultReadConfig; public bool HandleTempolessAnchor(Anchor anchor) => Configuration.TempolessAnchorPolicy switch diff --git a/ChartTools/IO/Chart/Configuration/Sessions/ChartSession.cs b/ChartTools/IO/Chart/Configuration/Sessions/ChartSession.cs index 6327fe44..922bd7d1 100644 --- a/ChartTools/IO/Chart/Configuration/Sessions/ChartSession.cs +++ b/ChartTools/IO/Chart/Configuration/Sessions/ChartSession.cs @@ -1,9 +1,10 @@ -using ChartTools.IO.Configuration; +using ChartTools.IO.Components; +using ChartTools.IO.Configuration; using ChartTools.IO.Formatting; namespace ChartTools.IO.Chart.Configuration.Sessions; -internal abstract class ChartSession(ComponentList components, FormattingRules? formatting) : Session(components, formatting) +internal abstract class ChartSession(FormattingRules? formatting) : Session(formatting) { public override abstract CommonChartConfiguration Configuration { get; } } diff --git a/ChartTools/IO/Chart/Serializing/SyncTrackSerializer.cs b/ChartTools/IO/Chart/Serializing/SyncTrackSerializer.cs index 32bbe774..1eaf1f62 100644 --- a/ChartTools/IO/Chart/Serializing/SyncTrackSerializer.cs +++ b/ChartTools/IO/Chart/Serializing/SyncTrackSerializer.cs @@ -6,5 +6,5 @@ namespace ChartTools.IO.Chart.Serializing; internal class SyncTrackSerializer(SyncTrack content, ChartWritingSession session) : TrackObjectGroupSerializer(ChartFormatting.SyncTrackHeader, content, session) { - protected override IEnumerable[] LaunchProviders() => new IEnumerable[] { new TempoProvider().ProvideFor(Content.Tempo, session), new TimeSignatureProvider().ProvideFor(Content.TimeSignatures, session) }; + protected override IEnumerable[] LaunchProviders() => [new TempoProvider().ProvideFor(Content.Tempo, session), new TimeSignatureProvider().ProvideFor(Content.TimeSignatures, session)]; } diff --git a/ChartTools/IO/Components/ComponentList.cs b/ChartTools/IO/Components/ComponentList.cs new file mode 100644 index 00000000..b5855cd2 --- /dev/null +++ b/ChartTools/IO/Components/ComponentList.cs @@ -0,0 +1,25 @@ +namespace ChartTools.IO.Components; + +public record ComponentList() +{ + public static ComponentList Global() => new() + { + Metadata = true, + SyncTrack = true, + GlobalEvents = true + }; + + public static ComponentList Full() => new() + { + Metadata = true, + SyncTrack = true, + GlobalEvents = true, + Instruments = InstrumentComponentList.Full() + }; + + public bool Metadata { get; set; } + public bool SyncTrack { get; set; } + public bool GlobalEvents { get; set; } + + public InstrumentComponentList Instruments { get; set; } = new(); +} diff --git a/ChartTools/IO/ComponentList.cs b/ChartTools/IO/Components/InstrumentComponentList.cs similarity index 63% rename from ChartTools/IO/ComponentList.cs rename to ChartTools/IO/Components/InstrumentComponentList.cs index e7be8df5..de160abe 100644 --- a/ChartTools/IO/ComponentList.cs +++ b/ChartTools/IO/Components/InstrumentComponentList.cs @@ -1,43 +1,37 @@ -namespace ChartTools.IO; +namespace ChartTools.IO.Components; [Flags] public enum DifficultySet : byte { - None = 0, - Easy = 1 << 0, + None = 0, + Easy = 1 << 0, Medium = 1 << 1, - Hard = 1 << 2, + Hard = 1 << 2, Expert = 1 << 3, All = Easy | Medium | Hard | Expert }; + public static class DifficultyExtensions { public static DifficultySet ToSet(this Difficulty difficulty) => (DifficultySet)(1 << (int)difficulty); } -public record ComponentList() +public record InstrumentComponentList() { - public static ComponentList Full() => new() - { - Metadata = true, - SyncTrack = true, - GlobalEvents = true, - Drums = DifficultySet.All, - LeadGuitar = DifficultySet.All, - CoopGuitar = DifficultySet.All, - RythmGuitar = DifficultySet.All, - Bass = DifficultySet.All, - GHLGuitar = DifficultySet.All, - GHLBass = DifficultySet.All, - Keys = DifficultySet.All, - Vocals = DifficultySet.All + public static InstrumentComponentList Full() => new() + { + Drums = DifficultySet.All, + LeadGuitar = DifficultySet.All, + CoopGuitar = DifficultySet.All, + RythmGuitar = DifficultySet.All, + Bass = DifficultySet.All, + GHLGuitar = DifficultySet.All, + GHLBass = DifficultySet.All, + Keys = DifficultySet.All, + Vocals = DifficultySet.All }; - public bool Metadata { get; set; } - public bool SyncTrack { get; set; } - public bool GlobalEvents { get; set; } - public DifficultySet Drums { get => _drums; @@ -101,38 +95,38 @@ public DifficultySet Vocals } private DifficultySet _vocals; - public ComponentList(InstrumentIdentity instrument, DifficultySet difficulties = DifficultySet.All) : this() + public InstrumentComponentList(InstrumentIdentity identity, DifficultySet difficulties = DifficultySet.All) : this() { - Validator.ValidateEnum(instrument); + Validator.ValidateEnum(identity); Validator.ValidateEnum(difficulties); - MapInstrument(instrument) = difficulties; + Map(identity) = difficulties; } - public ComponentList(StandardInstrumentIdentity instrument, DifficultySet difficulties = DifficultySet.All) : this() + public InstrumentComponentList(StandardInstrumentIdentity identity, DifficultySet difficulties = DifficultySet.All) : this() { - Validator.ValidateEnum(instrument); + Validator.ValidateEnum(identity); Validator.ValidateEnum(difficulties); - MapInstrument((InstrumentIdentity)instrument) = difficulties; + Map((InstrumentIdentity)identity) = difficulties; } - public ComponentList(GHLInstrumentIdentity instrument, DifficultySet difficulties = DifficultySet.All) : this() + public InstrumentComponentList(GHLInstrumentIdentity identity, DifficultySet difficulties = DifficultySet.All) : this() { - Validator.ValidateEnum(instrument); + Validator.ValidateEnum(identity); Validator.ValidateEnum(difficulties); - MapInstrument((InstrumentIdentity)instrument) = difficulties; + Map((InstrumentIdentity)identity) = difficulties; } - public ref DifficultySet MapInstrument(InstrumentIdentity instrument) + public ref DifficultySet Map(InstrumentIdentity instrument) { switch (instrument) { case InstrumentIdentity.Drums: return ref _drums; case InstrumentIdentity.LeadGuitar: - return ref _leadGuitar;2 + return ref _leadGuitar; case InstrumentIdentity.CoopGuitar: return ref _coopGuitar; case InstrumentIdentity.RhythmGuitar: @@ -143,7 +137,7 @@ public ref DifficultySet MapInstrument(InstrumentIdentity instrument) return ref _ghlGuitar; case InstrumentIdentity.GHLBass: return ref _ghlBass; - case InstrumentIdentity.Keys; + case InstrumentIdentity.Keys: return ref _keys; case InstrumentIdentity.Vocals: return ref _vocals; @@ -151,4 +145,16 @@ public ref DifficultySet MapInstrument(InstrumentIdentity instrument) throw new UndefinedEnumException(instrument); } } + + public ref DifficultySet Map(StandardInstrumentIdentity instrument) + { + Validator.ValidateEnum(instrument); + return ref Map((InstrumentIdentity)(instrument)); + } + + public ref DifficultySet Map(GHLInstrumentIdentity instrument) + { + Validator.ValidateEnum(instrument); + return ref Map((InstrumentIdentity)(instrument)); + } } diff --git a/ChartTools/IO/Configuration/Session.cs b/ChartTools/IO/Configuration/Session.cs index 8f2ee7fe..67a93069 100644 --- a/ChartTools/IO/Configuration/Session.cs +++ b/ChartTools/IO/Configuration/Session.cs @@ -3,13 +3,11 @@ namespace ChartTools.IO.Configuration; -internal abstract class Session(ComponentList components, FormattingRules? formatting) +internal abstract class Session(FormattingRules? formatting) { public abstract ICommonConfiguration Configuration { get; } public FormattingRules Formatting { get; set; } = formatting ?? new(); - public ComponentList Components { get; set; } = components; - public bool HandleDuplicate(uint position, string objectType, Func checkDuplicate) => Configuration.DuplicateTrackObjectPolicy switch { DuplicateTrackObjectPolicy.ThrowException => checkDuplicate() diff --git a/ChartTools/IO/Ini/IniFile.cs b/ChartTools/IO/Ini/IniFile.cs index fd960875..fb781591 100644 --- a/ChartTools/IO/Ini/IniFile.cs +++ b/ChartTools/IO/Ini/IniFile.cs @@ -14,7 +14,7 @@ public static class IniFile /// A new instance of if is , otherwise the same reference. public static Metadata ReadMetadata(string path, Metadata? existing = null) { - var reader = new IniFileReader(path, header => header.Equals(IniFormatting.Header, StringComparison.OrdinalIgnoreCase) ? new(existing) : null); + var reader = new IniFileReader(path, existing); reader.Read(); return reader.Parsers.TryGetFirst(out var parser) @@ -26,7 +26,7 @@ public static Metadata ReadMetadata(string path, Metadata? existing = null) /// A new instance of if is , otherwise the same reference. public static async Task ReadMetadataAsync(string path, Metadata? existing = null, CancellationToken cancellationToken = default) { - var reader = new IniFileReader(path, header => header.Equals(IniFormatting.Header, StringComparison.OrdinalIgnoreCase) ? new(existing) : null); + var reader = new IniFileReader(path, existing); await reader.ReadAsync(cancellationToken); return reader.Parsers.TryGetFirst(out var parser) diff --git a/ChartTools/IO/Ini/IniFileReader.cs b/ChartTools/IO/Ini/IniFileReader.cs index 9638629b..bc90e3db 100644 --- a/ChartTools/IO/Ini/IniFileReader.cs +++ b/ChartTools/IO/Ini/IniFileReader.cs @@ -1,10 +1,12 @@ -namespace ChartTools.IO.Ini; +using ChartTools.IO.Parsing; -internal class IniFileReader : TextFileReader +namespace ChartTools.IO.Ini; + +internal class IniFileReader(string path, Metadata? existing) : TextFileReader(path) { public override IEnumerable Parsers => base.Parsers.Cast(); - public IniFileReader(string path, Func parserGetter) : base(path, parserGetter) { } + protected override TextParser? GetParser(string header) => header.Equals(IniFormatting.Header, StringComparison.OrdinalIgnoreCase) ? new IniParser(existing) : null; protected override bool IsSectionStart(string line) => !line.StartsWith('['); } diff --git a/ChartTools/Instruments/Instrument.cs b/ChartTools/Instruments/Instrument.cs index e94cd2b0..b904154a 100644 --- a/ChartTools/Instruments/Instrument.cs +++ b/ChartTools/Instruments/Instrument.cs @@ -94,7 +94,7 @@ public InstrumentType InstrumentType /// /// Creates an array containing all tracks. /// - public virtual Track?[] GetTracks() => new Track?[] { Easy, Medium, Hard, Expert }; + public virtual Track?[] GetTracks() => [Easy, Medium, Hard, Expert]; /// /// Creates an array containing all tracks with data. /// diff --git a/ChartTools/Tracks/Track.cs b/ChartTools/Tracks/Track.cs index 32fde3c4..df344fcb 100644 --- a/ChartTools/Tracks/Track.cs +++ b/ChartTools/Tracks/Track.cs @@ -1,6 +1,7 @@ using ChartTools.Events; using ChartTools.IO; using ChartTools.IO.Chart; +using ChartTools.IO.Components; using ChartTools.IO.Configuration; using ChartTools.IO.Formatting; From a1a9cc62b202092b383e0d30c3b3aac448fb4e90 Mon Sep 17 00:00:00 2001 From: TheBoxyBear <15822255+TheBoxyBear@users.noreply.github.com> Date: Mon, 15 Jul 2024 16:31:32 -0400 Subject: [PATCH 6/7] #71 Add writing of instrument set through list --- ChartTools/IO/Chart/ChartFile.cs | 101 ++++++++++++++++++++++++++++--- 1 file changed, 93 insertions(+), 8 deletions(-) diff --git a/ChartTools/IO/Chart/ChartFile.cs b/ChartTools/IO/Chart/ChartFile.cs index f89ebf10..7acfc638 100644 --- a/ChartTools/IO/Chart/ChartFile.cs +++ b/ChartTools/IO/Chart/ChartFile.cs @@ -108,12 +108,68 @@ public static async Task ReadComponentsAsync(string path, ComponentList co { TInst? output = null; - foreach (var parser in reader.Parsers) - (output ??= new()).SetTrack(((TrackParser)parser).Result!); + foreach (var parser in reader.Parsers.Cast>()) + parser.ApplyToInstrument(output ??= new()); return output; } + private static InstrumentSet CreateInstrumentSetFromReader(ChartFileReader reader) + { + var instruments = new InstrumentSet(); + + foreach (var parser in reader.Parsers) + switch (parser) + { + case DrumsTrackParser drumsParser: + instruments.Drums ??= new(); + drumsParser.ApplyToInstrument(instruments.Drums); + break; + case StandardTrackParser standardParser: + var standardInst = instruments.Get(standardParser.Instrument); + + if (standardInst is null) + { + standardInst = new StandardInstrument(standardParser.Instrument); + instruments.Set(standardInst); + } + + standardParser.ApplyToInstrument(standardInst); + break; + case GHLTrackParser ghlParser: + var ghlInst = instruments.Get(ghlParser.Instrument); + + if (ghlInst is null) + { + ghlInst = new GHLInstrument(ghlParser.Instrument); + instruments.Set(ghlInst); + } + + ghlParser.ApplyToInstrument(ghlInst); + break; + } + + return instruments; + } + + public static InstrumentSet ReadInstruments(string path, InstrumentComponentList components, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) + { + var session = new ChartReadingSession(new() { Instruments = components }, config, formatting); + var reader = new ChartFileReader(path, session); + + reader.Read(); + return CreateInstrumentSetFromReader(reader); + } + + public static async Task ReadInstrumentsAsync(string path, InstrumentComponentList components, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) + { + var session = new ChartReadingSession(new() { Instruments = components }, config, formatting); + var reader = new ChartFileReader(path, session); + + await reader.ReadAsync(cancellationToken); + return CreateInstrumentSetFromReader(reader); + } + /// /// Reads an instrument from a chart file. /// @@ -455,12 +511,39 @@ public static async Task ReplaceComponentsAsync(string path, Song song, Componen await writer.WriteAsync(cancellationToken); } - private static ChartFileWriter GetInstrumentWriter(string path, Instrument? instrument, InstrumentIdentity identity, ChartWritingSession session) + private static ChartFileWriter GetInstrumentSetWriter(string path, InstrumentSet set, InstrumentComponentList components, ChartWritingSession session) + { + var serializers = new List>(); + var removedHeaders = new List(); + + foreach (var identity in EnumCache.Values) + FillInstrumentWriterData(set.Get(identity), identity, components.Map(identity), session, serializers, removedHeaders); + + return new(path, removedHeaders, [.. serializers]); + } + + public static void ReplaceInstrumentSet(string path, InstrumentSet set, InstrumentComponentList components, ChartWritingConfiguration? config = default, FormattingRules? formatting = default) + { + var session = new ChartWritingSession(config, formatting); + var writer = GetInstrumentSetWriter(path, set, components, new(config, formatting)); + + writer.Write(); + } + + public static async Task ReplaceInstrumentSetAsync(string path, InstrumentSet set, InstrumentComponentList components, ChartWritingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) + { + var session = new ChartWritingSession(config, formatting); + var writer = GetInstrumentSetWriter(path, set, components, new(config, formatting)); + + await writer.WriteAsync(cancellationToken); + } + + private static ChartFileWriter GetInstrumentWriter(string path, Instrument? instrument, InstrumentIdentity identity, DifficultySet diffs, ChartWritingSession session) { var removedHeaders = new List(); var serializers = new List>(); - FillInstrumentWriterData(instrument, identity, DifficultySet.All, session, serializers, removedHeaders); + FillInstrumentWriterData(instrument, identity, diffs, session, serializers, removedHeaders); return new(path, removedHeaders, [.. serializers]); } @@ -469,15 +552,15 @@ private static ChartFileWriter GetInstrumentWriter(string path, Instrument? inst /// Replaces an instrument in a file. /// /// Path of the file to write - public static void ReplaceInstrument(string path, Instrument instrument, ChartWritingConfiguration? config = default, FormattingRules? formatting = default) + public static void ReplaceInstrument(string path, Instrument instrument, DifficultySet diffs = DifficultySet.All, ChartWritingConfiguration? config = default, FormattingRules? formatting = default) { - var writer = GetInstrumentWriter(path, instrument, instrument.InstrumentIdentity, new(config, formatting)); + var writer = GetInstrumentWriter(path, instrument, instrument.InstrumentIdentity, diffs, new(config, formatting)); writer.Write(); } - public static async Task ReplaceInstrumentAsync(string path, Instrument instrument, ChartWritingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) + public static async Task ReplaceInstrumentAsync(string path, Instrument instrument, DifficultySet diffs = DifficultySet.All, ChartWritingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) { - var writer = GetInstrumentWriter(path, instrument, instrument.InstrumentIdentity, new(config, formatting)); + var writer = GetInstrumentWriter(path, instrument, instrument.InstrumentIdentity, diffs, new(config, formatting)); await writer.WriteAsync(cancellationToken); } @@ -491,12 +574,14 @@ private static ChartFileWriter GetTrackWriter(string path, Track track, ChartWri return new(path, null, new TrackSerializer(track, session)); } + [Obsolete($"Use {nameof(ReplaceInstrument)} with a {nameof(DifficultySet)}.")] public static void ReplaceTrack(string path, Track track, ChartWritingConfiguration? config = default, FormattingRules? formatting = default) { var writer = GetTrackWriter(path, track, new(config, formatting)); writer.Write(); } + [Obsolete($"Use {nameof(ReplaceInstrumentAsync)} with a {nameof(DifficultySet)}.")] public static async Task ReplaceTrackAsync(string path, Track track, ChartWritingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) { var writer = GetTrackWriter(path, track, new(config, formatting)); From 897dd5ed719ed00cd37d72be090c8f8bdfa403f4 Mon Sep 17 00:00:00 2001 From: TheBoxyBear <15822255+TheBoxyBear@users.noreply.github.com> Date: Mon, 15 Jul 2024 16:59:01 -0400 Subject: [PATCH 7/7] #71 Deprecate single instrument IO --- ChartTools/IO/Chart/ChartFile.cs | 176 +++++++----------------- ChartTools/Instruments/InstrumentSet.cs | 52 +++---- ChartTools/Tracks/Track.cs | 37 +++-- 3 files changed, 100 insertions(+), 165 deletions(-) diff --git a/ChartTools/IO/Chart/ChartFile.cs b/ChartTools/IO/Chart/ChartFile.cs index 7acfc638..c23f2cfe 100644 --- a/ChartTools/IO/Chart/ChartFile.cs +++ b/ChartTools/IO/Chart/ChartFile.cs @@ -101,19 +101,6 @@ public static async Task ReadComponentsAsync(string path, ComponentList co #endregion #region Instruments - /// - /// Combines the results from the parsers in a into an instrument. - /// - private static TInst? CreateInstrumentFromReader(ChartFileReader reader) where TInst : Instrument, new() where TChord : IChord, new() - { - TInst? output = null; - - foreach (var parser in reader.Parsers.Cast>()) - parser.ApplyToInstrument(output ??= new()); - - return output; - } - private static InstrumentSet CreateInstrumentSetFromReader(ChartFileReader reader) { var instruments = new InstrumentSet(); @@ -170,15 +157,7 @@ public static async Task ReadInstrumentsAsync(string path, Instru return CreateInstrumentSetFromReader(reader); } - /// - /// Reads an instrument from a chart file. - /// - /// Instance of containing all data about the given instrument - /// if the file contains no data for the given instrument - /// - /// Path of the file to read - /// Instrument to read - /// + [Obsolete($"Use {nameof(ReadInstruments)} with a component list.")] public static Instrument? ReadInstrument(string path, InstrumentIdentity instrument, DifficultySet difficulties = DifficultySet.All, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) { if (instrument == InstrumentIdentity.Drums) @@ -190,6 +169,7 @@ public static async Task ReadInstrumentsAsync(string path, Instru : throw new UndefinedEnumException(instrument); } + [Obsolete($"Use {nameof(ReadInstrumentsAsync)} with a component list.")] public static async Task ReadInstrumentAsync(string path, InstrumentIdentity instrument, DifficultySet difficulties = DifficultySet.All, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) { if (instrument == InstrumentIdentity.Drums) @@ -233,88 +213,42 @@ public static async Task ReadInstrumentsAsync(string path, Instru #endregion #region Drums - /// - /// Reads drums from a chart file. - /// - /// Instance of where TChord is containing all drums data - /// if the file contains no drums data - /// - /// Path of the file to read - /// - /// - public static Drums? ReadDrums(string path, DifficultySet difficulties = DifficultySet.All, ChartReadingConfiguration ? config = default, FormattingRules? formatting = default) - { - var session = new ChartReadingSession(new() { Instruments = new(InstrumentIdentity.Drums, difficulties) }, config, formatting); - var reader = new ChartFileReader(path, session); - - reader.Read(); - return CreateInstrumentFromReader(reader); - } + [Obsolete($"Use {nameof(ReadInstruments)} with a component list.")] + public static Drums? ReadDrums(string path, DifficultySet difficulties = DifficultySet.All, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) + => ReadInstruments(path, new(InstrumentIdentity.Drums), config, formatting).Drums; + [Obsolete($"Use {nameof(ReadInstrumentsAsync)} with a component list.")] public static async Task ReadDrumsAsync(string path, DifficultySet difficulties = DifficultySet.All, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) - { - var session = new ChartReadingSession(new() { Instruments = new(InstrumentIdentity.Drums) }, config, formatting); - var reader = new ChartFileReader(path, session); - - await reader.ReadAsync(cancellationToken); - return CreateInstrumentFromReader(reader); - } + => (await ReadInstrumentsAsync(path, new(InstrumentIdentity.Drums), config, formatting)).Drums; #endregion #region GHL - /// - /// Reads a Guitar Hero Live instrument from a chart file. - /// - /// Instance of where TChord is containing all data about the given instrument - /// if the file has no data for the given instrument - /// - /// Path of the file to read + [Obsolete($"Use {nameof(ReadInstruments)} with a component list.")] public static GHLInstrument? ReadInstrument(string path, GHLInstrumentIdentity instrument, DifficultySet difficulties = DifficultySet.All, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) - { - var session = new ChartReadingSession(new() { Instruments = new(instrument, difficulties) }, config, formatting); - var reader = new ChartFileReader(path, session); - - reader.Read(); - return CreateInstrumentFromReader(reader); - } + => ReadInstruments(path, new(instrument, difficulties), config, formatting).Get(instrument); + [Obsolete($"Use {nameof(ReadInstrumentsAsync)} with a component list.")] public static async Task ReadInstrumentAsync(string path, GHLInstrumentIdentity instrument, DifficultySet difficulties = DifficultySet.All, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) - { - var session = new ChartReadingSession(new() { Instruments = new(instrument, difficulties) }, config, formatting); - var reader = new ChartFileReader(path, session); - - await reader.ReadAsync(cancellationToken); - return CreateInstrumentFromReader(reader); - } + => (await ReadInstrumentsAsync(path, new(instrument, difficulties), config, formatting, cancellationToken)).Get(instrument); #endregion #region Standard + [Obsolete($"Use {nameof(ReadInstruments)} with a component list.")] public static StandardInstrument? ReadInstrument(string path, StandardInstrumentIdentity instrument, DifficultySet difficulties = DifficultySet.All, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) - { - var session = new ChartReadingSession(new() { Instruments = new(instrument, difficulties) }, config, formatting); - var reader = new ChartFileReader(path, session); - - reader.Read(); - return CreateInstrumentFromReader(reader); - } + => ReadInstruments(path, new(instrument, difficulties), config, formatting).Get(instrument); + [Obsolete($"Use {nameof(ReadInstrumentsAsync)} with a component list.")] public static async Task ReadInstrumentAsync(string path, StandardInstrumentIdentity instrument, DifficultySet difficulties = DifficultySet.All, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) - { - var session = new ChartReadingSession(new() { Instruments = new(instrument, difficulties) }, config, formatting); - var reader = new ChartFileReader(path, session); - - await reader.ReadAsync(cancellationToken); - return CreateInstrumentFromReader(reader); - } + => (await ReadInstrumentsAsync(path, new(instrument, difficulties), config, formatting, cancellationToken)).Get(instrument); #endregion #endregion #region Tracks - [Obsolete($"Use {nameof(ReadInstrument)} with a {nameof(DifficultySet)}.")] + [Obsolete($"Use {nameof(ReadInstruments)} with a component list.")] public static Track? ReadTrack(string path, InstrumentIdentity instrument, Difficulty difficulty, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) => ReadInstrument(path, instrument, difficulty.ToSet(), config, formatting)?.GetTrack(difficulty); - [Obsolete($"Use {nameof(ReadInstrumentAsync)} with a {nameof(DifficultySet)}.")] + [Obsolete($"Use {nameof(ReadInstrumentsAsync)} with a component list.")] public static async Task ReadTrackAsync(string path, InstrumentIdentity instrument, Difficulty difficulty, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) => (await ReadInstrumentAsync(path, instrument, difficulty.ToSet(), config, formatting, cancellationToken))?.GetTrack(difficulty); @@ -329,21 +263,21 @@ public static async Task> ReadDrumsTrackAsync(string path, Dif #endregion #region GHL - [Obsolete($"Use {nameof(ReadInstrument)} with a {nameof(DifficultySet)}.")] + [Obsolete($"Use {nameof(ReadInstruments)} with a component list.")] public static Track? ReadTrack(string path, GHLInstrumentIdentity instrument, Difficulty difficulty, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) => ReadInstrument(path, instrument, difficulty.ToSet(), config, formatting)?.GetTrack(difficulty); - [Obsolete($"Use {nameof(ReadInstrumentAsync)} with a {nameof(DifficultySet)}.")] + [Obsolete($"Use {nameof(ReadInstrumentsAsync)} with a component list.")] public static async Task?> ReadTrackAsync(string path, GHLInstrumentIdentity instrument, Difficulty difficulty, ChartReadingConfiguration? config, FormattingRules? formatting = default, CancellationToken cancellationToken = default) => (await ReadInstrumentAsync(path, instrument, difficulty.ToSet(), config, formatting, cancellationToken))?.GetTrack(difficulty); #endregion #region Standard - [Obsolete($"Use {nameof(ReadInstrument)} with a {nameof(DifficultySet)}.")] + [Obsolete($"Use {nameof(ReadInstruments)} with a component list.")] public static Track? ReadTrack(string path, StandardInstrumentIdentity instrument, Difficulty difficulty, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) => ReadInstrument(path, instrument, difficulty.ToSet(), config, formatting)?.GetTrack(difficulty); - [Obsolete($"Use {nameof(ReadInstrumentAsync)} with a {nameof(DifficultySet)}.")] + [Obsolete($"Use {nameof(ReadInstrumentsAsync)} with a component list.")] public static async Task?> ReadTrackAsync(string path, StandardInstrumentIdentity instrument, Difficulty difficulty, ChartReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) => (await ReadInstrumentAsync(path, instrument, difficulty.ToSet(), config, formatting, cancellationToken))?.GetTrack(difficulty); #endregion @@ -402,7 +336,8 @@ public static async Task> ReadGlobalEventsAsync(string path, C /// /// /// Token to request cancellation - public static async Task> ReadLyricsAsync(string path, CancellationToken cancellationToken = default) => (await ReadGlobalEventsAsync(path, cancellationToken)).GetLyrics(); + public static async Task> ReadLyricsAsync(string path, CancellationToken cancellationToken = default) + => (await ReadGlobalEventsAsync(path, cancellationToken)).GetLyrics(); #endregion #region Sync track @@ -434,18 +369,23 @@ public static async Task ReadSyncTrackAsync(string path, ChartReading #endregion #region Writing - private static void FillInstrumentWriterData(Instrument? inst, InstrumentIdentity identity, DifficultySet tracks, ChartWritingSession session, + private static void FillInstrumentsWriterData(InstrumentSet set, InstrumentComponentList components, ChartWritingSession session, ICollection> serializers, ICollection removedHeaders) { - // Only act on tracks specified in the component list - foreach (var diff in EnumCache.Values.Where(d => tracks.HasFlag(d.ToSet()))) + foreach (var identity in EnumCache.Values) { - var track = inst?.GetTrack(diff); + var tracks = components.Map(identity); + + // Only act on tracks specified in the component list + foreach (var diff in EnumCache.Values.Where(d => tracks.HasFlag(d.ToSet()))) + { + var track = set.Get(identity)?.GetTrack(diff); - if (track?.IsEmpty is not null or false) - serializers.Add(new TrackSerializer(track, session)); - else // No track data for the instrument and difficulty - removedHeaders.Add(ChartFormatting.Header(identity, diff)); + if (track?.IsEmpty is not null or false) + serializers.Add(new TrackSerializer(track, session)); + else // No track data for the instrument and difficulty + removedHeaders.Add(ChartFormatting.Header(identity, diff)); + } } } @@ -473,8 +413,7 @@ private static ChartFileWriter GetSongWriter(string path, Song song, ComponentLi removedHeaders.Add(ChartFormatting.GlobalEventHeader); } - foreach (var identity in EnumCache.Values) - FillInstrumentWriterData(song.Instruments.Get(identity), identity, components.Instruments.Map(identity), session, serializers, removedHeaders); + FillInstrumentsWriterData(song.Instruments, components.Instruments, session, serializers, removedHeaders); if (song.UnknownChartSections is not null) serializers.AddRange(song.UnknownChartSections.Select(s => new UnknownSectionSerializer(s.Header, s, session))); @@ -511,57 +450,48 @@ public static async Task ReplaceComponentsAsync(string path, Song song, Componen await writer.WriteAsync(cancellationToken); } - private static ChartFileWriter GetInstrumentSetWriter(string path, InstrumentSet set, InstrumentComponentList components, ChartWritingSession session) + private static ChartFileWriter GetInstrumentsWriter(string path, InstrumentSet set, InstrumentComponentList components, ChartWritingSession session) { var serializers = new List>(); var removedHeaders = new List(); - foreach (var identity in EnumCache.Values) - FillInstrumentWriterData(set.Get(identity), identity, components.Map(identity), session, serializers, removedHeaders); + FillInstrumentsWriterData(set, components, session, serializers, removedHeaders); return new(path, removedHeaders, [.. serializers]); } - public static void ReplaceInstrumentSet(string path, InstrumentSet set, InstrumentComponentList components, ChartWritingConfiguration? config = default, FormattingRules? formatting = default) + public static void ReplaceInstruments(string path, InstrumentSet set, InstrumentComponentList components, ChartWritingConfiguration? config = default, FormattingRules? formatting = default) { var session = new ChartWritingSession(config, formatting); - var writer = GetInstrumentSetWriter(path, set, components, new(config, formatting)); + var writer = GetInstrumentsWriter(path, set, components, new(config, formatting)); writer.Write(); } - public static async Task ReplaceInstrumentSetAsync(string path, InstrumentSet set, InstrumentComponentList components, ChartWritingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) + public static async Task ReplaceInstrumentsAsync(string path, InstrumentSet set, InstrumentComponentList components, ChartWritingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) { var session = new ChartWritingSession(config, formatting); - var writer = GetInstrumentSetWriter(path, set, components, new(config, formatting)); + var writer = GetInstrumentsWriter(path, set, components, new(config, formatting)); await writer.WriteAsync(cancellationToken); } - private static ChartFileWriter GetInstrumentWriter(string path, Instrument? instrument, InstrumentIdentity identity, DifficultySet diffs, ChartWritingSession session) - { - var removedHeaders = new List(); - var serializers = new List>(); - - FillInstrumentWriterData(instrument, identity, diffs, session, serializers, removedHeaders); - - return new(path, removedHeaders, [.. serializers]); - } - - /// - /// Replaces an instrument in a file. - /// - /// Path of the file to write + [Obsolete($"Use {nameof(ReadInstruments)} with a component list.")] public static void ReplaceInstrument(string path, Instrument instrument, DifficultySet diffs = DifficultySet.All, ChartWritingConfiguration? config = default, FormattingRules? formatting = default) { - var writer = GetInstrumentWriter(path, instrument, instrument.InstrumentIdentity, diffs, new(config, formatting)); - writer.Write(); + var set = new InstrumentSet(); + set.Set(instrument); + + ReplaceInstruments(path, set, new(instrument.InstrumentIdentity, diffs), config, formatting); } + [Obsolete($"Use {nameof(ReadInstrumentsAsync)} with a component list.")] public static async Task ReplaceInstrumentAsync(string path, Instrument instrument, DifficultySet diffs = DifficultySet.All, ChartWritingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) { - var writer = GetInstrumentWriter(path, instrument, instrument.InstrumentIdentity, diffs, new(config, formatting)); - await writer.WriteAsync(cancellationToken); + var set = new InstrumentSet(); + set.Set(instrument); + + await ReplaceInstrumentsAsync(path, set, new(instrument.InstrumentIdentity, diffs), config, formatting); } private static ChartFileWriter GetTrackWriter(string path, Track track, ChartWritingSession session) diff --git a/ChartTools/Instruments/InstrumentSet.cs b/ChartTools/Instruments/InstrumentSet.cs index 4365fe28..e8950409 100644 --- a/ChartTools/Instruments/InstrumentSet.cs +++ b/ChartTools/Instruments/InstrumentSet.cs @@ -14,6 +14,7 @@ public class InstrumentSet : IEnumerable /// Set of drums tracks /// public Drums? Drums { get; set; } + /// /// Set of Guitar Hero Live guitar tracks /// @@ -23,6 +24,7 @@ public GHLInstrument? GHLGuitar set => _ghlGuitar = value is null ? value : value with { InstrumentIdentity = GHLInstrumentIdentity.Guitar }; } private GHLInstrument? _ghlGuitar; + /// /// Set of Guitar Hero Live bass tracks /// @@ -32,6 +34,7 @@ public GHLInstrument? GHLBass set => _ghlBass = value is null ? value : value with { InstrumentIdentity = GHLInstrumentIdentity.Bass }; } private GHLInstrument? _ghlBass; + /// /// Set of lead guitar tracks /// @@ -41,6 +44,7 @@ public StandardInstrument? LeadGuitar set => _leadGuitar = value is null ? value : value with { InstrumentIdentity = StandardInstrumentIdentity.LeadGuitar }; } private StandardInstrument? _leadGuitar; + /// /// Set of rhythm guitar tracks /// @@ -50,6 +54,7 @@ public StandardInstrument? RhythmGuitar set => _rhythmGuitar = value is null ? value : value with { InstrumentIdentity = StandardInstrumentIdentity.RhythmGuitar }; } private StandardInstrument? _rhythmGuitar; + /// /// Set of coop guitar tracks /// @@ -59,6 +64,7 @@ public StandardInstrument? CoopGuitar set => _coopGuitar = value is null ? value : value with { InstrumentIdentity = StandardInstrumentIdentity.CoopGuitar }; } private StandardInstrument? _coopGuitar; + /// /// Set of bass tracks /// @@ -68,6 +74,7 @@ public StandardInstrument? Bass set => _bass = value is null ? value : value with { InstrumentIdentity = StandardInstrumentIdentity.Bass }; } private StandardInstrument? _bass; + /// /// Set of keyboard tracks /// @@ -77,6 +84,7 @@ public StandardInstrument? Keys set => _keys = value is null ? value : value with { InstrumentIdentity = StandardInstrumentIdentity.Keys }; } private StandardInstrument? _keys; + public Vocals? Vocals { get; set; } /// @@ -120,38 +128,36 @@ public StandardInstrument? Keys public IEnumerable Existing() => this.NonNull().Where(instrument => !instrument.IsEmpty); - public void Set(StandardInstrument instrument) + public void Set(Instrument instrument) { switch (instrument.InstrumentIdentity) { - case StandardInstrumentIdentity.LeadGuitar: - _leadGuitar = instrument; + case InstrumentIdentity.Drums: + Drums = (Drums)instrument; break; - case StandardInstrumentIdentity.RhythmGuitar: - _rhythmGuitar = instrument; + case InstrumentIdentity.Vocals: + Vocals = (Vocals)instrument; break; - case StandardInstrumentIdentity.CoopGuitar: - _coopGuitar = instrument; + case InstrumentIdentity.LeadGuitar: + _leadGuitar = (StandardInstrument)instrument; break; - case StandardInstrumentIdentity.Bass: - _bass = instrument; + case InstrumentIdentity.RhythmGuitar: + _rhythmGuitar = (StandardInstrument)instrument; break; - case StandardInstrumentIdentity.Keys: - _keys = instrument; + case InstrumentIdentity.CoopGuitar: + _coopGuitar = (StandardInstrument)instrument; break; - default: - throw new UndefinedEnumException(instrument.InstrumentIdentity); - } - } - public void Set(GHLInstrument instrument) - { - switch (instrument.InstrumentIdentity) - { - case GHLInstrumentIdentity.Guitar: - GHLGuitar = instrument; + case InstrumentIdentity.Bass: + _bass = (StandardInstrument)instrument; + break; + case InstrumentIdentity.Keys: + _keys = (StandardInstrument)instrument; + break; + case InstrumentIdentity.GHLGuitar: + GHLGuitar = (GHLInstrument)instrument; break; - case GHLInstrumentIdentity.Bass: - GHLBass = instrument; + case InstrumentIdentity.GHLBass: + GHLBass = (GHLInstrument)instrument; break; default: throw new UndefinedEnumException(instrument.InstrumentIdentity); diff --git a/ChartTools/Tracks/Track.cs b/ChartTools/Tracks/Track.cs index df344fcb..90be9772 100644 --- a/ChartTools/Tracks/Track.cs +++ b/ChartTools/Tracks/Track.cs @@ -1,7 +1,6 @@ using ChartTools.Events; using ChartTools.IO; using ChartTools.IO.Chart; -using ChartTools.IO.Components; using ChartTools.IO.Configuration; using ChartTools.IO.Formatting; @@ -72,62 +71,62 @@ internal IEnumerable SoloToStarPower(bool removeEvents) #region File reading #region Single file - [Obsolete($"Use {nameof(ChartFile.ReadInstrument)} with a difficulty set.")] + [Obsolete($"Use {nameof(ChartFile.ReadInstruments)} with a component list.")] public static Track FromFile(string path, InstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default) => ExtensionHandler.Read(path, (".chart", path => ChartFile.ReadTrack(path, instrument, difficulty, config?.Chart, formatting))); - [Obsolete($"Use {nameof(ChartFile.ReadInstrumentAsync)} with a difficulty set.")] + [Obsolete($"Use {nameof(ChartFile.ReadInstrumentsAsync)} with a component list.")] public static async Task FromFileAsync(string path, InstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) => await ExtensionHandler.ReadAsync(path, (".chart", path => ChartFile.ReadTrackAsync(path, instrument, difficulty, config?.Chart, formatting, cancellationToken))); - [Obsolete($"Use {nameof(ChartFile.ReadDrums)} with a difficulty set.")] + [Obsolete($"Use {nameof(ChartFile.ReadInstruments)} with a component list.")] public static Track FromFile(string path, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default) => ExtensionHandler.Read(path, (".chart", path => ChartFile.ReadDrumsTrack(path, difficulty, config?.Chart, formatting))); - [Obsolete($"Use {nameof(ChartFile.ReadDrumsAsync)} with a difficulty set.")] + [Obsolete($"Use {nameof(ChartFile.ReadInstrumentsAsync)} with a component list.")] public static async Task> FromFileAsync(string path, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) => await ExtensionHandler.ReadAsync(path, (".chart", path => ChartFile.ReadDrumsTrackAsync(path, difficulty, config?.Chart, formatting, cancellationToken))); - [Obsolete($"Use {nameof(ChartFile.ReadInstrument)} with a difficulty set.")] + [Obsolete($"Use {nameof(ChartFile.ReadInstruments)} with a component list.")] public static Track FromFile(string path, GHLInstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default) => ExtensionHandler.Read(path, (".chart", path => ChartFile.ReadTrack(path, instrument, difficulty, config?.Chart, formatting))); - [Obsolete($"Use {nameof(ChartFile.ReadInstrumentAsync)} with a difficulty set.")] + [Obsolete($"Use {nameof(ChartFile.ReadInstrumentsAsync)} with a component list.")] public static async Task> FromFileAsync(string path, GHLInstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) => await ExtensionHandler.ReadAsync(path, (".chart", path => ChartFile.ReadTrackAsync(path, instrument, difficulty, config?.Chart, formatting, cancellationToken))); - [Obsolete($"Use {nameof(ChartFile.ReadInstrument)} with a difficulty set.")] + [Obsolete($"Use {nameof(ChartFile.ReadInstruments)} with a component list.")] public static Track FromFile(string path, StandardInstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default) => ExtensionHandler.Read(path, (".chart", path => ChartFile.ReadTrack(path, instrument, difficulty, config?.Chart, formatting))); - [Obsolete($"Use {nameof(ChartFile.ReadInstrumentAsync)} with a difficulty set.")] + [Obsolete($"Use {nameof(ChartFile.ReadInstrumentsAsync)} with a component list.")] public static async Task> FromFileAsync(string path, StandardInstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, FormattingRules? formatting = default, CancellationToken cancellationToken = default) => await ExtensionHandler.ReadAsync(path, (".chart", path => ChartFile.ReadTrackAsync(path, instrument, difficulty, config?.Chart, formatting, cancellationToken))); #endregion #region Directory - [Obsolete($"Use {nameof(ChartFile.ReadInstrument)} with a {nameof(DifficultySet)} and {nameof(Metadata.Formatting)}.")] + [Obsolete($"Use {nameof(ChartFile.ReadInstruments)} with a component list and {nameof(Metadata.Formatting)}.")] public static DirectoryResult FromDirectory(string directory, InstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default) => DirectoryHandler.FromDirectory(directory, (path, formatting) => FromFile(path, instrument, difficulty, config, formatting)); - [Obsolete($"Use {nameof(ChartFile.ReadInstrumentAsync)} with a {nameof(DifficultySet)} and {nameof(Metadata.Formatting)}.")] + [Obsolete($"Use {nameof(ChartFile.ReadInstrumentsAsync)} with a component list and {nameof(Metadata.Formatting)}.")] public static async Task> FromDirectoryAsync(string directory, InstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, CancellationToken cancellationToken = default) => await DirectoryHandler.FromDirectoryAsync(directory, async (path, formatting) => await FromFileAsync(path, instrument, difficulty, config, formatting, cancellationToken), cancellationToken); - [Obsolete($"Use {nameof(ChartFile.ReadDrums)} with a {nameof(DifficultySet)} and {nameof(Metadata.Formatting)}.")] + [Obsolete($"Use {nameof(ChartFile.ReadInstruments)} with a component list and {nameof(Metadata.Formatting)}.")] public static DirectoryResult?> FromDirectory(string directory, Difficulty difficulty, ReadingConfiguration? config = default) => DirectoryHandler.FromDirectory(directory, (path, formatting) => FromFile(path, difficulty, config, formatting)); - [Obsolete($"Use {nameof(ChartFile.ReadDrumsAsync)} with a {nameof(DifficultySet)} and {nameof(Metadata.Formatting)}.")] + [Obsolete($"Use {nameof(ChartFile.ReadInstrumentsAsync)} with a component list and {nameof(Metadata.Formatting)}.")] public static async Task?>> FromDirectoryAsync(string directory, Difficulty difficulty, ReadingConfiguration? config = default, CancellationToken cancellationToken = default) => await DirectoryHandler.FromDirectoryAsync(directory, async (path, formatting) => await FromFileAsync(path, difficulty, config, formatting, cancellationToken), cancellationToken); - [Obsolete($"Use {nameof(ChartFile.ReadInstrument)} with a {nameof(DifficultySet)} and {nameof(Metadata.Formatting)}.")] + [Obsolete($"Use {nameof(ChartFile.ReadInstruments)} with a component list and {nameof(Metadata.Formatting)}.")] public static DirectoryResult?> FromDirectory(string directory, GHLInstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default) => DirectoryHandler.FromDirectory(directory, (path, formatting) => FromFile(path, instrument, difficulty, config, formatting)); - [Obsolete($"Use {nameof(ChartFile.ReadInstrumentAsync)} with a {nameof(DifficultySet)} and {nameof(Metadata.Formatting)}.")] + [Obsolete($"Use {nameof(ChartFile.ReadInstrumentsAsync)} with a component list and {nameof(Metadata.Formatting)}.")] public static async Task?>> FromDirectoryAsync(string directory, GHLInstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, CancellationToken cancellationToken = default) => await DirectoryHandler.FromDirectoryAsync(directory, async (path, formatting) => await FromFileAsync(path, instrument, difficulty, config, formatting, cancellationToken), cancellationToken); - [Obsolete($"Use {nameof(ChartFile.ReadInstrument)} with a {nameof(DifficultySet)} and {nameof(Metadata.Formatting)}.")] + [Obsolete($"Use {nameof(ChartFile.ReadInstruments)} with a component list and {nameof(Metadata.Formatting)}.")] public static DirectoryResult?> FromDirectory(string directory, StandardInstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default) => DirectoryHandler.FromDirectory(directory, (path, formatting) => FromFile(path, instrument, difficulty, config, formatting)); - [Obsolete($"Use {nameof(ChartFile.ReadInstrumentAsync)} with a {nameof(DifficultySet)} and {nameof(Metadata.Formatting)}.")] + [Obsolete($"Use {nameof(ChartFile.ReadInstrumentsAsync)} with a component list and {nameof(Metadata.Formatting)}.")] public static async Task?>> FromDirectoryAsync(string directory, StandardInstrumentIdentity instrument, Difficulty difficulty, ReadingConfiguration? config = default, CancellationToken cancellationToken = default) => await DirectoryHandler.FromDirectoryAsync(directory, async (path, formatting) => await FromFileAsync(path, instrument, difficulty, config, formatting, cancellationToken), cancellationToken); #endregion #endregion - [Obsolete($"Use {nameof(ChartFile.ReplaceTrack)}.")] + [Obsolete($"Use {nameof(ChartFile.ReplaceInstruments)} with a component list.")] public void ToFile(string path, WritingConfiguration? config = default, FormattingRules? formatting = default) => ExtensionHandler.Write(path, this, (".chart", (path, track) => ChartFile.ReplaceTrack(path, track, config?.Chart, formatting))); - [Obsolete($"Use {nameof(ChartFile.ReplaceTrackAsync)}.")] + [Obsolete($"Use {nameof(ChartFile.ReplaceInstrumentsAsync)} with a component list.")] public async Task ToFileAsync(string path, WritingConfiguration? config = default,FormattingRules? formatting = default, CancellationToken cancellationToken = default) => await ExtensionHandler.WriteAsync(path, this, (".chart", (path, track) => ChartFile.ReplaceTrackAsync(path, track, config?.Chart, formatting, cancellationToken))); public override string ToString() => Difficulty.ToString();