Skip to content

Commit

Permalink
Merge pull request #72 from TheBoxyBear/beta
Browse files Browse the repository at this point in the history
Bulk IO operations
  • Loading branch information
TheBoxyBear authored Jul 16, 2024
2 parents ca2c16b + 8a102e5 commit b45ad38
Show file tree
Hide file tree
Showing 18 changed files with 609 additions and 485 deletions.
682 changes: 274 additions & 408 deletions ChartTools/IO/Chart/ChartFile.cs

Large diffs are not rendered by default.

39 changes: 36 additions & 3 deletions ChartTools/IO/Chart/ChartFileReader.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// Reader of text file that sends read lines to subscribers of its events.
/// </summary>
internal class ChartFileReader : TextFileReader
internal class ChartFileReader(string path, ChartReadingSession session) : TextFileReader(path)
{
protected readonly ChartReadingSession session = session;

public override IEnumerable<ChartParser> Parsers => base.Parsers.Cast<ChartParser>();
public override bool DefinedSectionEnd => true;

public ChartFileReader(string path, Func<string, ChartParser?> 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);
Expand Down
33 changes: 31 additions & 2 deletions ChartTools/IO/Chart/ChartFormatting.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using ChartTools.IO.Chart.Entries;
using ChartTools.Extensions;
using ChartTools.IO.Chart.Entries;

namespace ChartTools.IO.Chart;

Expand Down Expand Up @@ -35,7 +36,7 @@ internal static class ChartFormatting
/// <summary>
/// Part names of <see cref="InstrumentIdentity"/> without the difficulty
/// </summary>
public static readonly Dictionary<InstrumentIdentity, string> InstrumentHeaderNames = new()
public static readonly IReadOnlyDictionary<InstrumentIdentity, string> InstrumentHeaderNames = new Dictionary<InstrumentIdentity, string>()
{
{ InstrumentIdentity.Drums, DrumsHeaderName },
{ InstrumentIdentity.GHLGuitar, "GHLGuitar" },
Expand All @@ -47,6 +48,34 @@ internal static class ChartFormatting
{ InstrumentIdentity.Keys, "Keyboard" }
};

/// <summary>
/// Headers for drums tracks
/// </summary>
public static readonly IReadOnlyDictionary<string, Difficulty> DrumsTrackHeaders =
EnumCache<Difficulty>.Values
.ToDictionary(diff => Header(DrumsHeaderName, diff));

/// <summary>
/// Headers for GHL tracks
/// </summary>
public static readonly IReadOnlyDictionary<string, (Difficulty, GHLInstrumentIdentity)> GHLTrackHeaders =
GetTrackCombinations(Enum.GetValues<GHLInstrumentIdentity>())
.ToDictionary(tuple => Header(tuple.instrument, tuple.difficulty));

/// <summary>
/// Headers for standard tracks
/// </summary>
public static readonly Dictionary<string, (Difficulty, StandardInstrumentIdentity)> StandardTrackHeaders =
GetTrackCombinations(Enum.GetValues<StandardInstrumentIdentity>())
.ToDictionary(tuple => Header((InstrumentIdentity)tuple.instrument, tuple.difficulty));

/// <summary>
/// Gets all the combinations of instruments and difficulties.
/// </summary>
/// <param name="instruments">Enum containing the instruments</param>
private static IEnumerable<(Difficulty difficulty, TInstEnum instrument)> GetTrackCombinations<TInstEnum>(IEnumerable<TInstEnum> instruments)
=> from difficulty in EnumCache<Difficulty>.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);
Expand Down
1 change: 0 additions & 1 deletion ChartTools/IO/Chart/ChartSectionSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,4 @@ static ChartSection()

DefaultReservedHeaders = new(headers);
}
public ChartSection() : base() { }
}
Original file line number Diff line number Diff line change
@@ -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
Expand Down
3 changes: 2 additions & 1 deletion ChartTools/IO/Chart/Configuration/Sessions/ChartSession.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
2 changes: 1 addition & 1 deletion ChartTools/IO/Chart/Serializing/SyncTrackSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ namespace ChartTools.IO.Chart.Serializing;
internal class SyncTrackSerializer(SyncTrack content, ChartWritingSession session)
: TrackObjectGroupSerializer<SyncTrack>(ChartFormatting.SyncTrackHeader, content, session)
{
protected override IEnumerable<TrackObjectEntry>[] LaunchProviders() => new IEnumerable<TrackObjectEntry>[] { new TempoProvider().ProvideFor(Content.Tempo, session), new TimeSignatureProvider().ProvideFor(Content.TimeSignatures, session) };
protected override IEnumerable<TrackObjectEntry>[] LaunchProviders() => [new TempoProvider().ProvideFor(Content.Tempo, session), new TimeSignatureProvider().ProvideFor(Content.TimeSignatures, session)];
}
25 changes: 25 additions & 0 deletions ChartTools/IO/Components/ComponentList.cs
Original file line number Diff line number Diff line change
@@ -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();
}
160 changes: 160 additions & 0 deletions ChartTools/IO/Components/InstrumentComponentList.cs
Original file line number Diff line number Diff line change
@@ -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));
}
}
2 changes: 1 addition & 1 deletion ChartTools/IO/Configuration/Session.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool> checkDuplicate) => Configuration.DuplicateTrackObjectPolicy switch
{
Expand Down
6 changes: 4 additions & 2 deletions ChartTools/IO/FileReader.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using ChartTools.Extensions.Collections;
using ChartTools.IO.Configuration;

namespace ChartTools.IO;

Expand All @@ -20,15 +21,16 @@ protected void CheckBusy()
public abstract void Dispose();
}

internal abstract class FileReader<T, TParser>(string path, Func<string, TParser?> parserGetter) : FileReader<T>(path) where TParser : FileParser<T>
internal abstract class FileReader<T, TParser>(string path) : FileReader<T>(path) where TParser : FileParser<T>
{
public record ParserContentGroup(TParser Parser, DelayedEnumerableSource<T> Source);

public override IEnumerable<TParser> Parsers => parserGroups.Select(g => g.Parser);

protected readonly List<ParserContentGroup> parserGroups = [];
protected readonly List<Task> parseTasks = [];
protected readonly Func<string, TParser?> parserGetter = parserGetter;

protected abstract TParser? GetParser(string header);

public override void Read()
{
Expand Down
4 changes: 2 additions & 2 deletions ChartTools/IO/Ini/IniFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public static class IniFile
/// <returns>A new instance of <see cref="Metadata"/> if <paramref name="existing"/> is <see langword="null"/>, otherwise the same reference.</returns>
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)
Expand All @@ -26,7 +26,7 @@ public static Metadata ReadMetadata(string path, Metadata? existing = null)
/// <returns>A new instance of <see cref="Metadata"/> if <paramref name="existing"/> is <see langword="null"/>, otherwise the same reference.</returns>
public static async Task<Metadata> 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)
Expand Down
8 changes: 5 additions & 3 deletions ChartTools/IO/Ini/IniFileReader.cs
Original file line number Diff line number Diff line change
@@ -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<IniParser> Parsers => base.Parsers.Cast<IniParser>();

public IniFileReader(string path, Func<string, IniParser?> 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('[');
}
Loading

0 comments on commit b45ad38

Please sign in to comment.