Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.chart Preparsing #255

Merged
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
10 changes: 10 additions & 0 deletions Assets/Script/Data/Difficulty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,15 @@ public static Difficulty FromChar(char diff) {
_ => throw new System.Exception("Unknown difficulty.")
};
}

public static string ToDifficultyString(Difficulty difficulty) {
return difficulty switch {
Difficulty.EASY => "Easy",
Difficulty.MEDIUM => "Medium",
Difficulty.HARD => "Hard",
Difficulty.EXPERT => "Expert",
Difficulty.EXPERT_PLUS => "Expert+",
};
}
}
}
3 changes: 3 additions & 0 deletions Assets/Script/Song/Preparsers.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

110 changes: 110 additions & 0 deletions Assets/Script/Song/Preparsers/ChartPreparser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
using YARG.Data;
using Debug = UnityEngine.Debug;

namespace YARG.Song.Preparsers {
public static class ChartPreparser {

private static readonly Regex ChartEventRegex = new Regex(@"(\d+)\s?=\s?[NSE]\s?((\d+\s?\d+)|\w+)", RegexOptions.Compiled);

private static readonly IReadOnlyDictionary<Difficulty, string> DifficultyLookup = new Dictionary<Difficulty, string>()
{
{ Difficulty.EASY, "Easy" },
{ Difficulty.MEDIUM, "Medium" },
{ Difficulty.HARD, "Hard" },
{ Difficulty.EXPERT, "Expert" }
};

private static readonly IReadOnlyDictionary<Instrument, string> InstrumentLookup = new Dictionary<Instrument, string>()
{
{ Instrument.GUITAR, "Single" },
{ Instrument.GUITAR_COOP, "DoubleGuitar" },
{ Instrument.BASS, "DoubleBass" },
{ Instrument.RHYTHM, "DoubleRhythm" },
{ Instrument.DRUMS, "Drums" },
{ Instrument.KEYS, "Keyboard" },
// { Instrument.GHLiveGuitar, "GHLGuitar" },
// { Instrument.GHLiveBass, "GHLBass" },
// { Instrument.GHLiveRhythm, "GHLRhythm" },
// { Instrument.GHLiveCoop, "GHLCoop" }
};

public static ulong GetAvailableTracks(byte[] chartData) {
using var stream = new MemoryStream(chartData);

using var reader = new StreamReader(stream);
return ReadStream(reader);
}

public static ulong GetAvailableTracks(SongEntry song) {
using var reader = File.OpenText(Path.Combine(song.Location, song.NotesFile));

return ReadStream(reader);
}

private static ulong ReadStream(StreamReader reader) {
ulong tracks = 0;

while (!reader.EndOfStream) {
string line = reader.ReadLine()?.Trim();
if (line is null) {
continue;
}

if (line.Length <= 0)
continue;

if (line[0] != '[' && line[^1] != ']') continue;

string headerName = line[1..^1];
if (reader.ReadLine()?.Trim() != "{") continue;

string eventLine = reader.ReadLine()?.Trim();
if (eventLine is null) {
continue;
}

// This track has an event in it!
if (!ChartEventRegex.IsMatch(eventLine) || !GetTrackFromHeader(headerName, out var track)) {
continue;
}

int shiftAmount = (int)track.instrument * 4 + (int)track.difficulty;
tracks |= (uint)(1 << shiftAmount);
}

return tracks;
}

private static bool GetTrackFromHeader(string header, out (Instrument instrument, Difficulty difficulty) track) {
var diffEnums = (Difficulty[]) Enum.GetValues(typeof(Difficulty));
var instrumentEnums = (Instrument[]) Enum.GetValues(typeof(Instrument));

foreach (var instrument in instrumentEnums) {
if (!InstrumentLookup.ContainsKey(instrument))
continue;

foreach (var difficulty in diffEnums) {
if(!DifficultyLookup.ContainsKey(difficulty))
continue;

var trackName = $"{DifficultyLookup[difficulty]}{InstrumentLookup[instrument]}";

if (header != trackName) {
continue;
}

track = (instrument, difficulty);
return true;
}
}

track = (Instrument.INVALID, (Difficulty)(-1));
return false;
}
}
}
3 changes: 3 additions & 0 deletions Assets/Script/Song/Preparsers/ChartPreparser.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions Assets/Script/Song/Scanning/SongScanThread.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Threading;
using UnityEngine;
using YARG.Serialization;
using YARG.Song.Preparsers;

namespace YARG.Song {
public class SongScanThread {
Expand Down Expand Up @@ -194,12 +195,18 @@ private static ScanResult ScanIniSong(string cache, string directory, out SongEn

var checksum = BitConverter.ToString(SHA1.Create().ComputeHash(bytes)).Replace("-", "");

var tracks = ulong.MaxValue;
if (notesFile == "notes.chart") {
tracks = ChartPreparser.GetAvailableTracks(bytes);
}

// We have a song.ini, notes file and audio. The song is scannable.
song = new IniSongEntry {
CacheRoot = cache,
Location = directory,
Checksum = checksum,
NotesFile = notesFile,
AvailableParts = tracks,
};

return ScanHelpers.ParseSongIni(Path.Combine(directory, "song.ini"), (IniSongEntry) song);
Expand Down
6 changes: 5 additions & 1 deletion Assets/Script/Song/SongCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class SongCache {
/// <summary>
/// The date in which the cache version is based on.
/// </summary>
private const int CACHE_VERSION = 23_05_05;
private const int CACHE_VERSION = 23_05_06;

private readonly string _folder;
private readonly string _cacheFile;
Expand Down Expand Up @@ -111,6 +111,8 @@ private static void WriteSongEntry(BinaryWriter writer, SongEntry song) {
writer.Write((int) difficulty.Key);
writer.Write(difficulty.Value);
}

writer.Write(song.AvailableParts);

switch (song) {
case ExtractedConSongEntry conSong:
Expand Down Expand Up @@ -172,6 +174,8 @@ private static SongEntry ReadSongEntry(BinaryReader reader) {
result.PartDifficulties.Add(part, difficulty);
}

result.AvailableParts = (ulong)reader.ReadInt64();

switch (type) {
case SongType.ExtractedRbCon:
CacheHelpers.ReadExtractedConData(reader, (ExtractedConSongEntry) result);
Expand Down
15 changes: 13 additions & 2 deletions Assets/Script/Song/Types/SongEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,20 @@ public abstract class SongEntry {

public Dictionary<Instrument, int> PartDifficulties { get; } = new();

public string Checksum { get; set; }
public ulong AvailableParts { get; set; }

public string Checksum { get; set; }
public string NotesFile { get; set; }
public string Location { get; set; }
public string Location { get; set; }

public bool HasInstrument(Instrument instrument) {
long instrumentBits = 0xF << (int)instrument * 4;
return (AvailableParts & (ulong)instrumentBits) != 0;
}

public bool HasPart(Instrument instrument, Difficulty difficulty) {
long instrumentBits = 0x1 << (int)instrument * 4 + (int)difficulty;
return (AvailableParts & (ulong)instrumentBits) != 0;
}
}
}
89 changes: 63 additions & 26 deletions Assets/Script/UI/DifficultySelect.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.AddressableAssets;
Expand Down Expand Up @@ -29,6 +31,7 @@ private enum State {

private int playerIndex;
private string[] instruments;
private Difficulty[] difficulties;
private State state;

private int optionCount;
Expand Down Expand Up @@ -144,10 +147,11 @@ public void Next() {
bool showExpertPlus = player.chosenInstrument == "drums"
|| player.chosenInstrument == "realDrums"
|| player.chosenInstrument == "ghDrums";
UpdateDifficulty(showExpertPlus);
UpdateDifficulty(player.chosenInstrument, showExpertPlus);
}
} else if (state == State.DIFFICULTY) {
player.chosenDifficulty = (Difficulty) selected;
player.chosenDifficulty = difficulties[selected];
Debug.Log(player.chosenDifficulty);
OnInstrumentSelection?.Invoke(player);
IncreasePlayerIndex();
} else if (state == State.VOCALS) {
Expand Down Expand Up @@ -217,15 +221,25 @@ private void UpdateInstrument() {
state = State.INSTRUMENT;

// Get allowed instruments
var allowedInstruments = player.inputStrategy.GetAllowedInstruments();
optionCount = allowedInstruments.Length + 1;
string[] allowedInstruments = player.inputStrategy.GetAllowedInstruments();

var availableInstruments = allowedInstruments
.Where(instrument => MainMenu.Instance.chosenSong
.HasInstrument(InstrumentHelper.FromStringName(instrument))).ToList();

optionCount = availableInstruments.Count + 1;

// Add to options
string[] ops = new string[6];
instruments = new string[allowedInstruments.Length];
for (int i = 0; i < allowedInstruments.Length; i++) {
instruments[i] = allowedInstruments[i];
ops[i] = allowedInstruments[i] switch {
var ops = new string[availableInstruments.Count + 1];
instruments = new string[availableInstruments.Count];

for (int i = 0; i < instruments.Length; i++) {
if (!MainMenu.Instance.chosenSong.HasInstrument(
InstrumentHelper.FromStringName(allowedInstruments[i]))) {
continue;
}
instruments[i] = availableInstruments[i];
ops[i] = availableInstruments[i] switch {
"drums" => "Drums",
"realDrums" => "Pro Drums",
"guitar" => "Guitar",
Expand All @@ -242,13 +256,17 @@ private void UpdateInstrument() {
_ => "Unknown"
};
}
ops[allowedInstruments.Length] = "Sit Out";
ops[^1] = "Sit Out";

// Set text and sprites
for (int i = 0; i < 6; i++) {
options[i].SetText(ops[i]);
options[i].SetText("");
options[i].SetSelected(false);

if (i < ops.Length) {
options[i].SetText(ops[i]);
}

if (i < instruments.Length) {
var sprite = Addressables.LoadAssetAsync<Sprite>($"FontSprites[{instruments[i]}]").WaitForCompletion();
options[i].SetImage(sprite);
Expand All @@ -260,31 +278,50 @@ private void UpdateInstrument() {
options[0].SetSelected(true);
}

private void UpdateDifficulty(bool showExpertPlus) {
private void UpdateDifficulty(string chosenInstrument, bool showExpertPlus) {
state = State.DIFFICULTY;

optionCount = 4;
string[] ops = {
"Easy",
"Medium",
"Hard",
"Expert",
null,
null
};
var instrument = InstrumentHelper.FromStringName(chosenInstrument);
var availableDifficulties = new List<Difficulty>();
for (int i = 0; i < (int)Difficulty.EXPERT_PLUS; i++) {
if (!MainMenu.Instance.chosenSong.HasPart(instrument, (Difficulty)i)) {
continue;
}
availableDifficulties.Add((Difficulty)i);
}

if (showExpertPlus) {
optionCount++;
ops[4] = "Expert+";
availableDifficulties.Add(Difficulty.EXPERT_PLUS);
}

optionCount = availableDifficulties.Count;

difficulties = new Difficulty[optionCount];
var ops = new string[optionCount];

for(int i = 0; i < optionCount; i++) {
ops[i] = availableDifficulties[i] switch {
Difficulty.EASY => "Easy",
Difficulty.MEDIUM => "Medium",
Difficulty.HARD => "Hard",
Difficulty.EXPERT => "Expert",
Difficulty.EXPERT_PLUS => "Expert+",
_ => "Unknown"
};
difficulties[i] = availableDifficulties[i];
}

for (int i = 0; i < 6; i++) {
options[i].SetText(ops[i]);
options[i].SetText("");
options[i].SetSelected(false);

if (i < ops.Length) {
options[i].SetText(ops[i]);
}
}

selected = 3;
options[3].SetSelected(true);
selected = optionCount - 1;
options[optionCount - 1].SetSelected(true);
}

private void UpdateVocalOptions() {
Expand Down