Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 15 additions & 13 deletions ChartTools/IO/Chart/ChartFileReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ public override IEnumerable<ChartParser> Parsers

public Metadata? ExistingMetadata { get; set; }

protected override ChartParser? GetParser(string header)
protected override ChartParser? GetParser(in ReadOnlyMemory<char> header)
{
switch (header)
string headerString = header.ToString();

switch (header.Span)
{
case ChartFormatting.MetadataHeader:
return Session.Components.Metadata ? new MetadataParser(ExistingMetadata) : null;
Expand All @@ -32,27 +34,27 @@ public override IEnumerable<ChartParser> Parsers
case ChartFormatting.SyncTrackHeader:
return Session.Components.SyncTrack ? new SyncTrackParser(Session) : null;
default:
if (ChartFormatting.DrumsTrackHeaders.TryGetValue(header, out Difficulty diff))
if (ChartFormatting.DrumsTrackHeaders.TryGetValue(headerString, 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))
? new DrumsTrackParser(diff, Session, in header) : null;
else if (ChartFormatting.GHLTrackHeaders.TryGetValue(headerString, 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))
? new GHLTrackParser(ghlTuple.Item1, ghlTuple.Item2, Session, in header) : null;
else if (ChartFormatting.StandardTrackHeaders.TryGetValue(headerString, out (Difficulty, StandardInstrumentIdentity) standardTuple))
return Session.Components.Instruments.Map(standardTuple.Item2).HasFlag(standardTuple.Item1.ToSet())
? new StandardTrackParser(standardTuple.Item1, standardTuple.Item2, Session, header) : null;
? new StandardTrackParser(standardTuple.Item1, standardTuple.Item2, Session, in header) : null;
else
{
return Session.Configuration.UnknownSectionPolicy == UnknownSectionPolicy.ThrowException
return Session.Configuration.UnknownSectionPolicy is UnknownSectionPolicy.ThrowException
? throw new Exception($"Unknown section with header \"{header}\". Consider using {UnknownSectionPolicy.Store} to avoid this error.")
: new UnknownSectionParser(Session, header);
: new UnknownSectionParser(Session, in header);
}
}
}

protected override bool IsSectionStart(string line)
=> line == "{";
protected override bool IsSectionStart(in ReadOnlySpan<char> line)
=> line is "{";

protected override bool IsSectionEnd(string line)
protected override bool IsSectionEnd(in ReadOnlySpan<char> line)
=> ChartFormatting.IsSectionEnd(line);
}
31 changes: 20 additions & 11 deletions ChartTools/IO/Chart/ChartFormatting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,13 @@ public static string Header(string instrumentName, Difficulty difficulty)
=> Header(difficulty.ToString() + instrumentName);
public static string Header(string name) => $"[{name}]";

public static string Line(string header, string? value)
=> value is null ? string.Empty : $" {header} = {value}";
public static string Line(in ReadOnlySpan<char> header, in ReadOnlySpan<char> value)
=> $" {header} = {value}";

/// <summary>
/// Gets the written data for a note.
/// </summary>
/// <param name="position">Position of the parent <see cref="LaneChord"/></param>
/// <param name="position">Position of the parent <see cref="Chord"/></param>
/// <param name="index">Value of <see cref="INote.Index"/></param>
/// <param name="sustain">Value of <see cref="ILaneNote.Sustain"/></param>
public static TrackObjectEntry NoteEntry(uint position, byte index, uint sustain)
Expand All @@ -107,13 +107,22 @@ public static TrackObjectEntry NoteEntry(uint position, byte index, uint sustain
public static string Float(float value)
=> ((int)(value * 1000)).ToString().Replace(".", "").Replace(",", "");

public static bool IsSectionEnd(string line)
=> line == "}";
public static bool IsSectionEnd(in ReadOnlySpan<char> line)
=> line is "}";

/// <summary>
/// Splits the data of an entry.
/// </summary>
/// <param name="data">Data portion of a <see cref="TrackObjectEntry"/></param>
internal static string[] SplitData(string data)
=> data.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries);
internal static void SplitData(ReadOnlySpan<char> line, out ReadOnlySpan<char> a, out ReadOnlySpan<char> b)
{
int spaceIndex = line.IndexOf(' ');

if (spaceIndex == -1)
{
a = line;
b = [];
}
else
{
a = line[..spaceIndex];
b = line[(spaceIndex + 1)..];
}
}
}
11 changes: 6 additions & 5 deletions ChartTools/IO/Chart/Entries/NoteData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ internal readonly ref struct NoteData
/// </summary>
/// <param name="data">Data section of the line in the file</param>
/// <exception cref="FormatException"/>
internal NoteData(string data)
internal NoteData(in ReadOnlySpan<char> data)
{
string[] split = data.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries);
ReadOnlySpan<char> a, b;
ChartFormatting.SplitData(data, out a, out b);

if (split.Length < 2)
if (b.IsEmpty)
throw new EntryException();

Index = ValueParser.Parse<byte>(split[0], "note index");
SustainLength = ValueParser.Parse<uint>(split[1], "sustain length");
Index = ValueParser.Parse<byte>(in a, "note index");
SustainLength = ValueParser.Parse<uint>(in b, "sustain length");
}
}
36 changes: 22 additions & 14 deletions ChartTools/IO/Chart/Entries/TrackObjectEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,41 +13,49 @@
/// <summary>
/// Type code of <see cref="ITrackObject"/>
/// </summary>
public string Type { get; }
public ReadOnlyMemory<char> Type { get; }

/// <summary>
/// Additional data
/// </summary>
public string Data { get; }
public ReadOnlyMemory<char> Data { get; }

public TrackObjectEntry(string line)
: this(line.AsMemory()) { }

/// <summary>
/// Creates an instance of see<see cref="TrackObjectEntry"/>.
/// </summary>
/// <param name="line">Line in the file</param>
/// <exception cref="LineException"/>
public TrackObjectEntry(string line)
public TrackObjectEntry(in ReadOnlyMemory<char> line)
{
TextEntry entry = new(line);

if (entry.Value is null)
throw new LineException(line, new FormatException("Line has no object data."));
if (entry.Value.IsEmpty)
throw new LineException(line.ToString(), new FormatException("Line has no object data."));

string[] split = entry.Value.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries);
int spaceIndex = entry.Value.Span.IndexOf(' ');

if (split.Length < 2)
throw new LineException(line, new EntryException());
if (spaceIndex == -1)
throw new LineException(line.ToString(), new EntryException());

Type = split[0];
Data = split[1];
Type = entry.Value[0..spaceIndex];
Data = entry.Value[(spaceIndex + 1)..];

Position = ValueParser.Parse<uint>(entry.Key, "position");
Position = ValueParser.Parse<uint>(entry.Key.Span, "position");
}

public TrackObjectEntry(uint position, string type, string data)
: this(position, type.AsMemory(), data.AsMemory()) { }

public TrackObjectEntry(uint position, in ReadOnlyMemory<char> type, in ReadOnlyMemory<char> data)
{
Position = position;
Type = type;
Data = data;
Type = type;
Data = data;
}

public override string ToString() => ChartFormatting.Line(Position.ToString(), $"{Type} {Data}");
public override string ToString()
=> ChartFormatting.Line(Position.ToString(), $"{Type} {Data}");
}
5 changes: 4 additions & 1 deletion ChartTools/IO/Chart/Parsing/ChartParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@

namespace ChartTools.IO.Chart.Parsing;

internal abstract class ChartParser(ChartReadingSession session, string header)
internal abstract class ChartParser(ChartReadingSession session, in ReadOnlyMemory<char> header)
: TextParser(header), ISongAppliable
{
public ChartReadingSession Session { get; } = session;

public abstract void ApplyToSong(Song song);

protected override Exception GetHandleException(in ReadOnlyMemory<char> item, Exception innerException)
=> new SectionException(Header.ToString(), GetHandleInnerException(item, innerException));
}
4 changes: 2 additions & 2 deletions ChartTools/IO/Chart/Parsing/DrumsTrackParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

namespace ChartTools.IO.Chart.Parsing;

internal class DrumsTrackParser(Difficulty difficulty, ChartReadingSession session, string header)
: TrackParser<DrumsChord>(difficulty, session, header)
internal class DrumsTrackParser(Difficulty difficulty, ChartReadingSession session, in ReadOnlyMemory<char> header)
: TrackParser<DrumsChord>(difficulty, session, in header)
{
public override void ApplyToSong(Song song)
{
Expand Down
5 changes: 3 additions & 2 deletions ChartTools/IO/Chart/Parsing/GHLTrackParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@

namespace ChartTools.IO.Chart.Parsing;

internal class GHLTrackParser(Difficulty difficulty, GHLInstrumentIdentity instrument, ChartReadingSession session, string header)
: VariableInstrumentTrackParser<GHLChord, GHLInstrumentIdentity>(difficulty, instrument, session, header)
internal class GHLTrackParser(
Difficulty difficulty, GHLInstrumentIdentity instrument, ChartReadingSession session, in ReadOnlyMemory<char> header)
: VariableInstrumentTrackParser<GHLChord, GHLInstrumentIdentity>(difficulty, instrument, session, in header)
{
public override void ApplyToSong(Song song)
{
Expand Down
11 changes: 6 additions & 5 deletions ChartTools/IO/Chart/Parsing/GlobalEventParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@

namespace ChartTools.IO.Chart.Parsing;

internal class GlobalEventParser(ChartReadingSession session) : ChartParser(session, ChartFormatting.GlobalEventHeader)
internal class GlobalEventParser(ChartReadingSession session)
: ChartParser(session, ChartFormatting.GlobalEventHeader.AsMemory())
{
public override List<GlobalEvent> Result
=> GetResult(result);
=> GetResult(m_result);

private readonly List<GlobalEvent> result = [];
private readonly List<GlobalEvent> m_result = [];

protected override void HandleItem(string line)
protected override void HandleItem(in ReadOnlyMemory<char> line)
{
TrackObjectEntry entry = new(line);
result.Add(new(entry.Position, entry.Data.Trim('"')));
m_result.Add(new(entry.Position, entry.Data.Trim('"').ToString()));
}

public override void ApplyToSong(Song song)
Expand Down
64 changes: 34 additions & 30 deletions ChartTools/IO/Chart/Parsing/MetadataParser.cs
Original file line number Diff line number Diff line change
@@ -1,88 +1,92 @@
namespace ChartTools.IO.Chart.Parsing;

internal class MetadataParser(Metadata? existing = null) : ChartParser(null! /* Session not used */, ChartFormatting.MetadataHeader)
internal class MetadataParser(Metadata? existing = null)
: ChartParser(null! /* Session not used */, ChartFormatting.MetadataHeader.AsMemory())
{
public override Metadata Result => GetResult(result);
private readonly Metadata result = existing ?? new();
public override Metadata Result
=> GetResult(m_result);

protected override void HandleItem(string line)
private readonly Metadata m_result = existing ?? new();

protected override void HandleItem(in ReadOnlyMemory<char> line)
{
TextEntry entry = new(line);
string? value = entry.Value?.Trim('"');
ReadOnlySpan<char> value = entry.Value.Span.Trim('"');

switch (entry.Key)
switch (entry.Key.Span)
{
case ChartFormatting.Title:
result.Title = value;
m_result.Title = value.ToString();
break;
case ChartFormatting.Artist:
result.Artist = value;
m_result.Artist = value.ToString();
break;
case ChartFormatting.Charter:
result.Charter.Name = value;
m_result.Charter.Name = value.ToString();
break;
case ChartFormatting.Album:
result.Album = value;
m_result.Album = value.ToString();
break;
case ChartFormatting.Year:
result.Year = ValueParser.Parse<ushort>(value?.TrimStart(','), "year");
m_result.Year = ValueParser.Parse<ushort>(value.TrimStart(','), "year");
break;
case ChartFormatting.AudioOffset:
result.AudioOffset = TimeSpan.FromMilliseconds(ValueParser.Parse<float>(value, "audio offset") * 1000);
m_result.AudioOffset = TimeSpan.FromMilliseconds(ValueParser.Parse<float>(value, "audio offset") * 1000);
break;
case ChartFormatting.Difficulty:
result.Difficulty = ValueParser.Parse<sbyte>(value, "difficulty");
m_result.Difficulty = ValueParser.Parse<sbyte>(value, "difficulty");
break;
case ChartFormatting.PreviewStart:
result.PreviewStart = ValueParser.Parse<uint>(value, "preview start");
m_result.PreviewStart = ValueParser.Parse<uint>(value, "preview start");
break;
case ChartFormatting.PreviewEnd:
result.PreviewEnd = ValueParser.Parse<uint>(value, "preview end");
m_result.PreviewEnd = ValueParser.Parse<uint>(value, "preview end");
break;
case ChartFormatting.Genre:
result.Genre = value;
m_result.Genre = value.ToString();
break;
case ChartFormatting.MediaType:
result.MediaType = value;
m_result.MediaType = value.ToString();
break;
case ChartFormatting.MusicStream:
result.Streams.Music = value;
m_result.Streams.Music = value.ToString();
break;
case ChartFormatting.GuitarStream:
result.Streams.Guitar = value;
m_result.Streams.Guitar = value.ToString();
break;
case ChartFormatting.BassStream:
result.Streams.Bass = value;
m_result.Streams.Bass = value.ToString();
break;
case ChartFormatting.RhythmStream:
result.Streams.Rhythm = value;
m_result.Streams.Rhythm = value.ToString();
break;
case ChartFormatting.KeysStream:
result.Streams.Keys = value;
m_result.Streams.Keys = value.ToString();
break;
case ChartFormatting.DrumStream:
result.Streams.Drum = value;
m_result.Streams.Drum = value.ToString();
break;
case ChartFormatting.Drum2Stream:
result.Streams.Drum2 = value;
m_result.Streams.Drum2 = value.ToString();
break;
case ChartFormatting.Drum3Stream:
result.Streams.Drum3 = value;
m_result.Streams.Drum3 = value.ToString();
break;
case ChartFormatting.Drum4Stream:
result.Streams.Drum4 = value;
m_result.Streams.Drum4 = value.ToString();
break;
case ChartFormatting.VocalStream:
result.Streams.Vocals = value;
m_result.Streams.Vocals = value.ToString();
break;
case ChartFormatting.CrowdStream:
result.Streams.Crowd = value;
m_result.Streams.Crowd = value.ToString();
break;
default:
result.UnidentifiedData.Add(new() { Key = entry.Key, Value = entry.Value, Origin = FileType.Chart });
m_result.UnidentifiedData.Add(new() { Key = entry.Key.ToString(), Value = value.ToString(), Origin = FileType.Chart });
break;
}
}

public override void ApplyToSong(Song song) => song.Metadata = Result;
public override void ApplyToSong(Song song)
=> song.Metadata = Result;
}
5 changes: 3 additions & 2 deletions ChartTools/IO/Chart/Parsing/StandardTrackParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@

namespace ChartTools.IO.Chart.Parsing;

internal class StandardTrackParser(Difficulty difficulty, StandardInstrumentIdentity instrument, ChartReadingSession session, string header)
: VariableInstrumentTrackParser<StandardChord, StandardInstrumentIdentity>(difficulty, instrument, session, header)
internal class StandardTrackParser(
Difficulty difficulty, StandardInstrumentIdentity instrument, ChartReadingSession session, in ReadOnlyMemory<char> header)
: VariableInstrumentTrackParser<StandardChord, StandardInstrumentIdentity>(difficulty, instrument, session, in header)
{
public override void ApplyToSong(Song song)
{
Expand Down
Loading
Loading