diff --git a/ChartTools/IO/Chart/ChartFile.cs b/ChartTools/IO/Chart/ChartFile.cs index a1fbf848..c23f2cfe 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; @@ -43,36 +44,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,72 +63,124 @@ 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 ReadComponentsAsync(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. - /// - private static TInst? CreateInstrumentFromReader(ChartFileReader reader) where TInst : Instrument, new() where TChord : IChord, new() + private static InstrumentSet CreateInstrumentSetFromReader(ChartFileReader reader) { - TInst? output = null; + var instruments = new InstrumentSet(); foreach (var parser in reader.Parsers) - (output ??= new()).SetTrack(((TrackParser)parser).Result!); + 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 output; + return instruments; } - /// - /// 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 - /// - /// - /// - /// - public static Instrument? ReadInstrument(string path, InstrumentIdentity instrument, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) + 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); + } + + [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) - 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) + + [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) - 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); } + #region Vocals /// /// Reads vocals from the global events in a chart file. @@ -167,8 +190,10 @@ 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,323 +211,116 @@ 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. - /// - /// 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, ChartReadingConfiguration? config = default, FormattingRules? formatting = default) - { - var session = new ChartReadingSession(config, formatting ?? new()); - var reader = new ChartFileReader(path, header => GetAnyDrumsTrackParser(header, 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)); + #region Drums + [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; - await reader.ReadAsync(cancellationToken); - return CreateInstrumentFromReader(reader); - } + [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) + => (await ReadInstrumentsAsync(path, new(InstrumentIdentity.Drums), config, formatting)).Drums; #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. - /// - /// 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 - /// - 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)); - - 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)); + #region GHL + [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) + => ReadInstruments(path, new(instrument, difficulties), config, formatting).Get(instrument); - await reader.ReadAsync(cancellationToken); - return CreateInstrumentFromReader(reader); - } + [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) + => (await ReadInstrumentsAsync(path, new(instrument, difficulties), config, formatting, cancellationToken)).Get(instrument); #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)); - - 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)); + #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) + => ReadInstruments(path, new(instrument, difficulties), config, formatting).Get(instrument); - await reader.ReadAsync(cancellationToken); - return CreateInstrumentFromReader(reader); - } + [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) + => (await ReadInstrumentsAsync(path, new(instrument, difficulties), config, formatting, cancellationToken)).Get(instrument); #endregion #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(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); - 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(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); - 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)); - /// - /// - /// - /// + [Obsolete($"Use {nameof(ReadDrums)} with a {nameof(DifficultySet)}.")] 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)); + => ReadDrums(path, difficulty.ToSet(), config, formatting)?.GetTrack(difficulty) ?? new(); - reader.Read(); - return reader.Parsers.TryGetFirstOfType(out DrumsTrackParser? parser) ? parser!.Result! : 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) - { - 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)); - - 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 - /// - /// 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)); - /// - /// - /// - /// - /// - 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)); - 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) - { - 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)); + #region GHL + [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); - await reader.ReadAsync(cancellationToken); - return reader.Parsers.TryGetFirstOfType(out GHLTrackParser? parser) ? parser!.Result : new(); - } + [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 - /// - /// 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)); - - /// - /// - /// - /// - /// - 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)); - - 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) - { - 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)); + #region Standard + [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); - await reader.ReadAsync(cancellationToken); - return reader.Parsers.TryGetFirstOfType(out StandardTrackParser? parser) ? parser!.Result! : new(); - } + [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 #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! : []; } /// @@ -512,25 +330,25 @@ 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. /// /// /// 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 - /// - /// 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; + #region Sync track /// /// /// - 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 +359,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(); } @@ -551,101 +369,131 @@ public static async Task ReadSyncTrackAsync(string path, ChartReading #endregion #region Writing - /// - /// Writes a song to a chart file. - /// - /// Path of the file to write - /// Song to write - public static void WriteSong(string path, Song song, ChartWritingConfiguration? config = default) - { - var writer = GetSongWriter(path, song, new(config, song.Metadata.Formatting)); - writer.Write(); - } - public static async Task WriteSongAsync(string path, Song song, ChartWritingConfiguration? config = default, CancellationToken cancellationToken = default) + private static void FillInstrumentsWriterData(InstrumentSet set, InstrumentComponentList components, ChartWritingSession session, + ICollection> serializers, ICollection removedHeaders) { - var writer = GetSongWriter(path, song, new(config, song.Metadata.Formatting)); - await writer.WriteAsync(cancellationToken); + foreach (var identity in EnumCache.Values) + { + 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)); + } + } } - private static ChartFileWriter GetSongWriter(string path, Song song, ChartWritingSession session) + + private static ChartFileWriter GetSongWriter(string path, Song song, ComponentList components, ChartWritingSession session) { - var instruments = song.Instruments.NonNull().ToArray(); - var serializers = new List>(instruments.Length + 2); var removedHeaders = new List(); + var serializers = 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); + if (components.Metadata) + serializers.Add(new MetadataSerializer(song.Metadata)); - 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)); - - foreach (var instrument in instruments) + if (components.SyncTrack) { - var instrumentName = ChartFormatting.InstrumentHeaderNames[instrument.InstrumentIdentity]; - var tracks = instrument.GetExistingTracks().ToArray(); + if (!song.SyncTrack.IsEmpty) + serializers.Add(new SyncTrackSerializer(song.SyncTrack, session)); + else + removedHeaders.Add(ChartFormatting.SyncTrackHeader); + } - 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))); + if (components.GlobalEvents) + { + if (song.GlobalEvents.Count > 0) + serializers.Add(new GlobalEventSerializer(song.GlobalEvents, session)); + else + removedHeaders.Add(ChartFormatting.GlobalEventHeader); } + 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))); - return new(path, removedHeaders, serializers.ToArray()); + return new(path, removedHeaders, [.. serializers]); } /// - /// Replaces an instrument in a file. + /// Writes a song to a chart file. /// /// Path of the file to write - public static void ReplaceInstrument(string path, Instrument instrument, ChartWritingConfiguration? config = default, FormattingRules? formatting = default) + /// Song to write + public static void WriteSong(string path, Song song, ChartWritingConfiguration? config = default) + { + 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, ComponentList.Full(), new(config, song.Metadata.Formatting)); + await writer.WriteAsync(cancellationToken); + } + + public static void ReplaceComponents(string path, Song song, ComponentList components, ChartWritingConfiguration? config = default) { - var writer = GetInstrumentWriter(path, instrument, new(config, formatting ?? new())); + var writer = GetSongWriter(path, song, components, new(config, song.Metadata.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 ReplaceComponentsAsync(string path, Song song, ComponentList components, ChartWritingConfiguration? config = default, CancellationToken cancellationToken = default) { - var writer = GetInstrumentWriter(path, instrument, new(config, formatting ?? new())); + var writer = GetSongWriter(path, song, components, new(config, song.Metadata.Formatting)); await writer.WriteAsync(cancellationToken); } - private static ChartFileWriter GetInstrumentWriter(string path, Instrument instrument, ChartWritingSession session) + + private static ChartFileWriter GetInstrumentsWriter(string path, InstrumentSet set, InstrumentComponentList components, ChartWritingSession session) { - if (!Enum.IsDefined(instrument.InstrumentIdentity)) - throw new ArgumentException("Instrument cannot be written because its identity is unknown.", nameof(instrument)); + var serializers = new List>(); + var removedHeaders = new List(); - var instrumentName = ChartFormatting.InstrumentHeaderNames[instrument.InstrumentIdentity]; - var tracks = instrument.GetExistingTracks().ToArray(); + FillInstrumentsWriterData(set, components, session, serializers, removedHeaders); - 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, removedHeaders, [.. serializers]); } - public static void ReplaceTrack(string path, Track track, ChartWritingConfiguration? config = default, FormattingRules? formatting = default) + public static void ReplaceInstruments(string path, InstrumentSet set, InstrumentComponentList components, ChartWritingConfiguration? config = default, FormattingRules? formatting = default) { - var writer = GetTrackWriter(path, track, new(config, formatting ?? new())); + var session = new ChartWritingSession(config, formatting); + var writer = GetInstrumentsWriter(path, set, components, new(config, formatting)); + writer.Write(); } - public static async Task ReplaceTrackAsync(string path, Track track, 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 writer = GetTrackWriter(path, track, new(config, formatting ?? new())); + var session = new ChartWritingSession(config, formatting); + var writer = GetInstrumentsWriter(path, set, components, new(config, formatting)); + await writer.WriteAsync(cancellationToken); } + + [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 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 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) { if (track.ParentInstrument is null) @@ -656,6 +504,22 @@ 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)); + await writer.WriteAsync(cancellationToken); + } + + private static ChartFileWriter GetMetadataWriter(string path, Metadata metadata) => new(path, null, new MetadataSerializer(metadata)); + /// /// Replaces the metadata in a file. /// @@ -666,7 +530,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. @@ -678,12 +543,14 @@ 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)); + + private static ChartFileWriter GetSyncTrackWriter(string path, SyncTrack syncTrack, ChartWritingSession session) => new(path, null, new SyncTrackSerializer(syncTrack, session)); /// /// Replaces the sync track in a file. @@ -693,16 +560,15 @@ 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(); } - /// + 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/Chart/ChartFileReader.cs b/ChartTools/IO/Chart/ChartFileReader.cs index d27b3e66..f09cc194 100644 --- a/ChartTools/IO/Chart/ChartFileReader.cs +++ b/ChartTools/IO/Chart/ChartFileReader.cs @@ -1,16 +1,49 @@ -using ChartTools.IO.Chart.Parsing; +using ChartTools.IO.Chart.Configuration.Sessions; +using ChartTools.IO.Chart.Parsing; +using ChartTools.IO.Components; +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.Instruments.Drums.HasFlag(diff.ToSet()) + ? new DrumsTrackParser(diff, session, header) : null; + else if (ChartFormatting.GHLTrackHeaders.TryGetValue(header, out (Difficulty, GHLInstrumentIdentity) ghlTuple)) + 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.Instruments.Map(standardTuple.Item2).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..dc5292da 100644 --- a/ChartTools/IO/Chart/Configuration/Sessions/ChartReadingSession.cs +++ b/ChartTools/IO/Chart/Configuration/Sessions/ChartReadingSession.cs @@ -1,10 +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(ChartReadingConfiguration? config, FormattingRules? formatting) : ChartSession(formatting) +internal class ChartReadingSession(ComponentList components, ChartReadingConfiguration? config, FormattingRules? 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 7dd56c68..922bd7d1 100644 --- a/ChartTools/IO/Chart/Configuration/Sessions/ChartSession.cs +++ b/ChartTools/IO/Chart/Configuration/Sessions/ChartSession.cs @@ -1,4 +1,5 @@ -using ChartTools.IO.Configuration; +using ChartTools.IO.Components; +using ChartTools.IO.Configuration; using ChartTools.IO.Formatting; namespace ChartTools.IO.Chart.Configuration.Sessions; 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/Components/InstrumentComponentList.cs b/ChartTools/IO/Components/InstrumentComponentList.cs new file mode 100644 index 00000000..de160abe --- /dev/null +++ b/ChartTools/IO/Components/InstrumentComponentList.cs @@ -0,0 +1,160 @@ +namespace ChartTools.IO.Components; + +[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 static class DifficultyExtensions +{ + public static DifficultySet ToSet(this Difficulty difficulty) => (DifficultySet)(1 << (int)difficulty); +} + +public record InstrumentComponentList() +{ + 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 DifficultySet Drums + { + get => _drums; + set => _drums = value; + } + private DifficultySet _drums; + + public DifficultySet LeadGuitar + { + get => _leadGuitar; + set => _leadGuitar = value; + } + private DifficultySet _leadGuitar; + + public DifficultySet CoopGuitar + { + get => _coopGuitar; + set => _coopGuitar = value; + } + private DifficultySet _coopGuitar; + + public DifficultySet RythmGuitar + { + get => _rythmGuitar; + set => _rythmGuitar = value; + } + private DifficultySet _rythmGuitar; + + public DifficultySet Bass + { + get => _bass; + set => _bass = value; + } + private DifficultySet _bass; + + public DifficultySet GHLGuitar + { + get => _ghlGuitar; + set => _ghlGuitar = value; + } + private DifficultySet _ghlGuitar; + + public DifficultySet GHLBass + { + get => _ghlBass; + set => _ghlBass = value; + } + private DifficultySet _ghlBass; + + public DifficultySet Keys + { + get => _keys; + set => _keys = value; + } + private DifficultySet _keys; + + public DifficultySet Vocals + { + get => _vocals; + set => _vocals = value; + } + private DifficultySet _vocals; + + public InstrumentComponentList(InstrumentIdentity identity, DifficultySet difficulties = DifficultySet.All) : this() + { + Validator.ValidateEnum(identity); + Validator.ValidateEnum(difficulties); + + Map(identity) = difficulties; + } + + public InstrumentComponentList(StandardInstrumentIdentity identity, DifficultySet difficulties = DifficultySet.All) : this() + { + Validator.ValidateEnum(identity); + Validator.ValidateEnum(difficulties); + + Map((InstrumentIdentity)identity) = difficulties; + } + + public InstrumentComponentList(GHLInstrumentIdentity identity, DifficultySet difficulties = DifficultySet.All) : this() + { + Validator.ValidateEnum(identity); + Validator.ValidateEnum(difficulties); + + Map((InstrumentIdentity)identity) = difficulties; + } + + public ref DifficultySet Map(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); + } + } + + 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 2242dc28..67a93069 100644 --- a/ChartTools/IO/Configuration/Session.cs +++ b/ChartTools/IO/Configuration/Session.cs @@ -6,7 +6,7 @@ namespace ChartTools.IO.Configuration; internal abstract class Session(FormattingRules? formatting) { public abstract ICommonConfiguration Configuration { get; } - public FormattingRules? Formatting { get; set; } = formatting; + public FormattingRules Formatting { get; set; } = formatting ?? new(); 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 73e10f00..33af6d37 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/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/IO/TextFileReader.cs b/ChartTools/IO/TextFileReader.cs index 6c0ef8cd..6040621b 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; @@ -30,7 +28,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) { 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/Instruments/InstrumentSet.cs b/ChartTools/Instruments/InstrumentSet.cs index 361b57d7..e8950409 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; @@ -13,6 +14,7 @@ public class InstrumentSet : IEnumerable /// Set of drums tracks /// public Drums? Drums { get; set; } + /// /// Set of Guitar Hero Live guitar tracks /// @@ -22,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 /// @@ -31,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 /// @@ -40,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 /// @@ -49,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 /// @@ -58,6 +64,7 @@ public StandardInstrument? CoopGuitar set => _coopGuitar = value is null ? value : value with { InstrumentIdentity = StandardInstrumentIdentity.CoopGuitar }; } private StandardInstrument? _coopGuitar; + /// /// Set of bass tracks /// @@ -67,6 +74,7 @@ public StandardInstrument? Bass set => _bass = value is null ? value : value with { InstrumentIdentity = StandardInstrumentIdentity.Bass }; } private StandardInstrument? _bass; + /// /// Set of keyboard tracks /// @@ -76,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; } /// @@ -119,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/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 { diff --git a/ChartTools/Tracks/Track.cs b/ChartTools/Tracks/Track.cs index c0c6acef..90be9772 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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.ReadTrack)} with {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.ReadTrackAsync)} with {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.ReadDrumsTrack)} with {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.ReadDrumsTrackAsync)} with {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.ReadTrack)} with {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.ReadTrackAsync)} with {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.ReadTrack)} with {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.ReadTrackAsync)} with {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)}.")] - 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.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)}.")] - 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))); + [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(); }