diff --git a/BAKKA-Editor/Chart.cs b/BAKKA-Editor/Chart.cs index 0e3c89a..db17d9a 100644 --- a/BAKKA-Editor/Chart.cs +++ b/BAKKA-Editor/Chart.cs @@ -8,295 +8,295 @@ namespace BAKKA_Editor { - internal class Chart + internal class Chart + { + private static readonly CultureInfo _defaultParsingCulture = CultureInfo.InvariantCulture; + public List Notes { get; set; } + public List Gimmicks { get; set; } + /// + /// Offset in seconds. + /// + public double Offset { get; set; } + /// + /// Movie offset in seconds + /// + public double MovieOffset { get; set; } + String SongFileName { get; set; } + List TimeEvents { get; set; } + public bool HasInitEvents { - private static readonly CultureInfo _defaultParsingCulture = CultureInfo.InvariantCulture; - public List Notes { get; set; } - public List Gimmicks { get; set; } - /// - /// Offset in seconds. - /// - public double Offset { get; set; } - /// - /// Movie offset in seconds - /// - public double MovieOffset { get; set; } - String SongFileName { get; set; } - List TimeEvents { get; set; } - public bool HasInitEvents - { - get - { - return TimeEvents != null && - TimeEvents.Count > 0 && - Gimmicks.Count(x => x.Measure == 0 && x.GimmickType == GimmickType.BpmChange) >= 1 && - Gimmicks.Count(x => x.Measure == 0 && x.GimmickType == GimmickType.TimeSignatureChange) >= 1; - } - } - public bool IsSaved { get; set; } - - public Chart() - { - Notes = new(); - Gimmicks = new(); - Offset = 0; - MovieOffset = 0; - SongFileName = ""; - IsSaved = true; - } + get + { + return TimeEvents != null && + TimeEvents.Count > 0 && + Gimmicks.Count(x => x.Measure == 0 && x.GimmickType == GimmickType.BpmChange) >= 1 && + Gimmicks.Count(x => x.Measure == 0 && x.GimmickType == GimmickType.TimeSignatureChange) >= 1; + } + } + public bool IsSaved { get; set; } - public bool ParseFile(string filename) - { - var file = File.ReadAllLines(filename); + public Chart() + { + Notes = new(); + Gimmicks = new(); + Offset = 0; + MovieOffset = 0; + SongFileName = ""; + IsSaved = true; + } - int index = 0; + public bool ParseFile(string filename) + { + var file = File.ReadAllLines(filename); - do - { - var line = file[index]; + int index = 0; - var path = Utils.GetTag(line, "#MUSIC_FILE_PATH "); - if (path != null) - SongFileName = path; - var offset = Utils.GetTag(line, "#OFFSET"); - if (offset != null) - Offset = Convert.ToDouble(offset, _defaultParsingCulture); + do + { + var line = file[index]; - offset = Utils.GetTag(line, "#MOVIEOFFSET"); - if (offset != null) - MovieOffset = Convert.ToDouble(offset, _defaultParsingCulture); + var path = Utils.GetTag(line, "#MUSIC_FILE_PATH "); + if (path != null) + SongFileName = path; + var offset = Utils.GetTag(line, "#OFFSET"); + if (offset != null) + Offset = Convert.ToDouble(offset, _defaultParsingCulture); + offset = Utils.GetTag(line, "#MOVIEOFFSET"); + if (offset != null) + MovieOffset = Convert.ToDouble(offset, _defaultParsingCulture); - if (line.Contains("#BODY")) - { - index++; - break; - } - } while (++index < file.Length); + if (line.Contains("#BODY")) + { + index++; + break; + } - int lineNum; - Gimmick gimmickTemp; - Note noteTemp; - Dictionary notesByLine = new(); - Dictionary refByLine = new(); - for (int i = index; i < file.Length; i++) - { - if (String.IsNullOrWhiteSpace(file[i])) - continue; + } while (++index < file.Length); - var parsed = file[i].Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); - NoteBase temp = new(); - temp.BeatInfo = new BeatInfo(Convert.ToInt32(parsed[0], _defaultParsingCulture), Convert.ToInt32(parsed[1], _defaultParsingCulture)); - temp.GimmickType = (GimmickType)Convert.ToInt32(parsed[2], _defaultParsingCulture); + int lineNum; + Gimmick gimmickTemp; + Note noteTemp; + Dictionary notesByLine = new(); + Dictionary refByLine = new(); + for (int i = index; i < file.Length; i++) + { + if (String.IsNullOrWhiteSpace(file[i])) + continue; - switch (temp.GimmickType) - { - case GimmickType.NoGimmick: - noteTemp = new Note(temp.BeatInfo); - noteTemp.NoteType = (NoteType)Convert.ToInt32(parsed[3], _defaultParsingCulture); - lineNum = Convert.ToInt32(parsed[4], _defaultParsingCulture); - noteTemp.Position = Convert.ToInt32(parsed[5], _defaultParsingCulture); - noteTemp.Size = Convert.ToInt32(parsed[6], _defaultParsingCulture); - noteTemp.HoldChange = Convert.ToBoolean(Convert.ToInt32(parsed[7], _defaultParsingCulture)); - if (noteTemp.NoteType == NoteType.MaskAdd || noteTemp.NoteType == NoteType.MaskRemove) - { - noteTemp.MaskFill = (MaskType)Convert.ToInt32(parsed[8], _defaultParsingCulture); - } - else if (noteTemp.NoteType == NoteType.HoldStartNoBonus || - noteTemp.NoteType == NoteType.HoldJoint || - noteTemp.NoteType == NoteType.HoldStartBonusFlair) - { - refByLine[lineNum] = Convert.ToInt32(parsed[8], _defaultParsingCulture); - } - Notes.Add(noteTemp); - notesByLine[lineNum] = Notes.Last(); - break; - case GimmickType.BpmChange: - gimmickTemp = new Gimmick(temp.BeatInfo, temp.GimmickType); - gimmickTemp.BPM = Convert.ToDouble(parsed[3], _defaultParsingCulture); - Gimmicks.Add(gimmickTemp); - break; - case GimmickType.TimeSignatureChange: - gimmickTemp = new Gimmick(temp.BeatInfo, temp.GimmickType); - gimmickTemp.TimeSig = new TimeSignature() { Upper = Convert.ToInt32(parsed[3], _defaultParsingCulture), Lower = parsed.Length == 5 ? Convert.ToInt32(parsed[4], _defaultParsingCulture) : 4 }; - Gimmicks.Add(gimmickTemp); - break; - case GimmickType.HiSpeedChange: - gimmickTemp = new Gimmick(temp.BeatInfo, temp.GimmickType); - gimmickTemp.HiSpeed = Convert.ToDouble(parsed[3], _defaultParsingCulture); - Gimmicks.Add(gimmickTemp); - break; - case GimmickType.ReverseStart: - case GimmickType.ReverseMiddle: - case GimmickType.ReverseEnd: - case GimmickType.StopStart: - case GimmickType.StopEnd: - default: - Gimmicks.Add(new Gimmick(temp.BeatInfo, temp.GimmickType)); - break; - } + var parsed = file[i].Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + NoteBase temp = new(); + temp.BeatInfo = new BeatInfo(Convert.ToInt32(parsed[0], _defaultParsingCulture), Convert.ToInt32(parsed[1], _defaultParsingCulture)); + temp.GimmickType = (GimmickType)Convert.ToInt32(parsed[2], _defaultParsingCulture); + switch (temp.GimmickType) + { + case GimmickType.NoGimmick: + noteTemp = new Note(temp.BeatInfo); + noteTemp.NoteType = (NoteType)Convert.ToInt32(parsed[3], _defaultParsingCulture); + lineNum = Convert.ToInt32(parsed[4], _defaultParsingCulture); + noteTemp.Position = Convert.ToInt32(parsed[5], _defaultParsingCulture); + noteTemp.Size = Convert.ToInt32(parsed[6], _defaultParsingCulture); + noteTemp.HoldChange = Convert.ToBoolean(Convert.ToInt32(parsed[7], _defaultParsingCulture)); + if (noteTemp.NoteType == NoteType.MaskAdd || noteTemp.NoteType == NoteType.MaskRemove) + { + noteTemp.MaskFill = (MaskType)Convert.ToInt32(parsed[8], _defaultParsingCulture); } - - // Generate hold references - for (int i = 0; i < Notes.Count; i++) + else if (noteTemp.NoteType == NoteType.HoldStartNoBonus || + noteTemp.NoteType == NoteType.HoldJoint || + noteTemp.NoteType == NoteType.HoldStartBonusFlair) { - if (refByLine.ContainsKey(i)) - { - Notes[i].NextNote = notesByLine[refByLine[i]]; - Notes[i].NextNote.PrevNote = Notes[i]; - } + refByLine[lineNum] = Convert.ToInt32(parsed[8], _defaultParsingCulture); } + Notes.Add(noteTemp); + notesByLine[lineNum] = Notes.Last(); + break; + case GimmickType.BpmChange: + gimmickTemp = new Gimmick(temp.BeatInfo, temp.GimmickType); + gimmickTemp.BPM = Convert.ToDouble(parsed[3], _defaultParsingCulture); + Gimmicks.Add(gimmickTemp); + break; + case GimmickType.TimeSignatureChange: + gimmickTemp = new Gimmick(temp.BeatInfo, temp.GimmickType); + gimmickTemp.TimeSig = new TimeSignature() { Upper = Convert.ToInt32(parsed[3], _defaultParsingCulture), Lower = parsed.Length == 5 ? Convert.ToInt32(parsed[4], _defaultParsingCulture) : 4 }; + Gimmicks.Add(gimmickTemp); + break; + case GimmickType.HiSpeedChange: + gimmickTemp = new Gimmick(temp.BeatInfo, temp.GimmickType); + gimmickTemp.HiSpeed = Convert.ToDouble(parsed[3], _defaultParsingCulture); + Gimmicks.Add(gimmickTemp); + break; + case GimmickType.ReverseStart: + case GimmickType.ReverseMiddle: + case GimmickType.ReverseEnd: + case GimmickType.StopStart: + case GimmickType.StopEnd: + default: + Gimmicks.Add(new Gimmick(temp.BeatInfo, temp.GimmickType)); + break; + } - RecalcTime(); + } - IsSaved = true; - return true; + // Generate hold references + for (int i = 0; i < Notes.Count; i++) + { + if (refByLine.ContainsKey(i)) + { + Notes[i].NextNote = notesByLine[refByLine[i]]; + Notes[i].NextNote.PrevNote = Notes[i]; } + } - public bool WriteFile(string filename, bool setSave = true) - { - using (StreamWriter sw = new StreamWriter(new FileStream(filename, FileMode.Create), new UTF8Encoding(false))) - { - // LF line ending - sw.NewLine = "\n"; + RecalcTime(); - sw.WriteLine("#MUSIC_SCORE_ID 0"); - sw.WriteLine("#MUSIC_SCORE_VERSION 0"); - sw.WriteLine("#GAME_VERSION "); - sw.WriteLine($"#MUSIC_FILE_PATH {SongFileName}"); - sw.WriteLine($"#OFFSET {Offset:F6}"); - sw.WriteLine($"#MOVIEOFFSET {MovieOffset:F6}"); - sw.WriteLine("#BODY"); + IsSaved = true; + return true; + } - foreach (var gimmick in Gimmicks) - { - sw.Write($"{gimmick.BeatInfo.Measure,4:F0}{gimmick.BeatInfo.Beat,5:F0}{((int)gimmick.GimmickType),5:F0}"); - switch (gimmick.GimmickType) - { - case GimmickType.BpmChange: - sw.WriteLine($" {gimmick.BPM:F6}"); - break; - case GimmickType.TimeSignatureChange: - sw.WriteLine($"{gimmick.TimeSig.Upper,5:F0}{gimmick.TimeSig.Lower,5:F0}"); - break; - case GimmickType.HiSpeedChange: - sw.WriteLine($" {gimmick.HiSpeed:F6}"); - break; - default: - sw.WriteLine(""); - break; - } - } + public bool WriteFile(string filename, bool setSave = true) + { + using (StreamWriter sw = new StreamWriter(new FileStream(filename, FileMode.Create), new UTF8Encoding(false))) + { + // LF line ending + sw.NewLine = "\n"; - foreach (var note in Notes) - { - sw.Write($"{note.BeatInfo.Measure,4:F0}{note.BeatInfo.Beat,5:F0}{((int)note.GimmickType),5:F0}{(int)note.NoteType,5:F0}"); - sw.Write($"{Notes.IndexOf(note),5:F0}{note.Position,5:F0}{note.Size,5:F0}{Convert.ToInt32(note.HoldChange, _defaultParsingCulture),5:F0}"); - if (note.IsMask) - sw.Write($"{(int)note.MaskFill,5:F0}"); - if (note.NextNote != null) - sw.Write($"{Notes.IndexOf(note.NextNote),5:F0}"); - sw.WriteLine(""); - } - } + sw.WriteLine("#MUSIC_SCORE_ID 0"); + sw.WriteLine("#MUSIC_SCORE_VERSION 0"); + sw.WriteLine("#GAME_VERSION "); + sw.WriteLine($"#MUSIC_FILE_PATH {SongFileName}"); + sw.WriteLine($"#OFFSET {Offset:F6}"); + sw.WriteLine($"#MOVIEOFFSET {MovieOffset:F6}"); + sw.WriteLine("#BODY"); - IsSaved = setSave; - return true; + foreach (var gimmick in Gimmicks) + { + sw.Write($"{gimmick.BeatInfo.Measure,4:F0}{gimmick.BeatInfo.Beat,5:F0}{((int)gimmick.GimmickType),5:F0}"); + switch (gimmick.GimmickType) + { + case GimmickType.BpmChange: + sw.WriteLine($" {gimmick.BPM:F6}"); + break; + case GimmickType.TimeSignatureChange: + sw.WriteLine($"{gimmick.TimeSig.Upper,5:F0}{gimmick.TimeSig.Lower,5:F0}"); + break; + case GimmickType.HiSpeedChange: + sw.WriteLine($" {gimmick.HiSpeed:F6}"); + break; + default: + sw.WriteLine(""); + break; + } } - public void RecalcTime() + foreach (var note in Notes) { - Gimmicks = Gimmicks.OrderBy(x => x.Measure).ToList(); - var timeSig = Gimmicks.FirstOrDefault(x => x.GimmickType == GimmickType.TimeSignatureChange && x.Measure == 0.0f); - var bpm = Gimmicks.FirstOrDefault(x => x.GimmickType == GimmickType.BpmChange && x.Measure == 0.0f); - if (timeSig == null || bpm == null) - return; // Cannot calculate times without either starting value + sw.Write($"{note.BeatInfo.Measure,4:F0}{note.BeatInfo.Beat,5:F0}{((int)note.GimmickType),5:F0}{(int)note.NoteType,5:F0}"); + sw.Write($"{Notes.IndexOf(note),5:F0}{note.Position,5:F0}{note.Size,5:F0}{Convert.ToInt32(note.HoldChange, _defaultParsingCulture),5:F0}"); + if (note.IsMask) + sw.Write($"{(int)note.MaskFill,5:F0}"); + if (note.NextNote != null) + sw.Write($"{Notes.IndexOf(note.NextNote),5:F0}"); + sw.WriteLine(""); + } + } - TimeEvents = new(); - for (int i = 0; i < Gimmicks.Count; i++) - { - var evt = TimeEvents.FirstOrDefault(x => x.BeatInfo.MeasureDecimal == Gimmicks[i].BeatInfo.MeasureDecimal); + IsSaved = setSave; + return true; + } - if (Gimmicks[i].GimmickType == GimmickType.BpmChange) - { - if (evt == null) - { - TimeEvents.Add(new Gimmick() - { - BeatInfo = new BeatInfo(Gimmicks[i].BeatInfo), - BPM = Gimmicks[i].BPM, - TimeSig = new TimeSignature(timeSig.TimeSig) - }); - } - else - { - evt.BPM = Gimmicks[i].BPM; - } - bpm = Gimmicks[i]; - } - if (Gimmicks[i].GimmickType == GimmickType.TimeSignatureChange) - { - if (evt == null) - { - TimeEvents.Add(new Gimmick() - { - BeatInfo = new BeatInfo(Gimmicks[i].BeatInfo), - BPM = bpm.BPM, - TimeSig = new TimeSignature(Gimmicks[i].TimeSig) - }); - } - else - { - evt.TimeSig = new TimeSignature(Gimmicks[i].TimeSig); - } - timeSig = Gimmicks[i]; - } - } + public void RecalcTime() + { + Gimmicks = Gimmicks.OrderBy(x => x.Measure).ToList(); + var timeSig = Gimmicks.FirstOrDefault(x => x.GimmickType == GimmickType.TimeSignatureChange && x.Measure == 0.0f); + var bpm = Gimmicks.FirstOrDefault(x => x.GimmickType == GimmickType.BpmChange && x.Measure == 0.0f); + if (timeSig == null || bpm == null) + return; // Cannot calculate times without either starting value + + TimeEvents = new(); + for (int i = 0; i < Gimmicks.Count; i++) + { + var evt = TimeEvents.FirstOrDefault(x => x.BeatInfo.MeasureDecimal == Gimmicks[i].BeatInfo.MeasureDecimal); - // Run through all time events and generate valid start times - TimeEvents[0].StartTime = Offset * 1000.0; - for (int i = 1; i < TimeEvents.Count; i++) + if (Gimmicks[i].GimmickType == GimmickType.BpmChange) + { + if (evt == null) + { + TimeEvents.Add(new Gimmick() { - TimeEvents[i].StartTime = ((TimeEvents[i].Measure - TimeEvents[i - 1].Measure) * (4.0f * TimeEvents[i - 1].TimeSig.Ratio * (60000.0 / TimeEvents[i - 1].BPM))) + TimeEvents[i - 1].StartTime; - } + BeatInfo = new BeatInfo(Gimmicks[i].BeatInfo), + BPM = Gimmicks[i].BPM, + TimeSig = new TimeSignature(timeSig.TimeSig) + }); + } + else + { + evt.BPM = Gimmicks[i].BPM; + } + bpm = Gimmicks[i]; } - /* - ((60000.0 / evt.BPM) * 4.0 * evt.TimeSig.Ratio) * measure = time - time / ((60000.0 / evt.BPM) * 4.0 * evt.TimeSig.Ratio) = measure - */ - - /// - /// Translate clock time to beats - /// - /// Current timestamp in ms - /// - public BeatInfo GetBeat(float time) + if (Gimmicks[i].GimmickType == GimmickType.TimeSignatureChange) { - if (TimeEvents == null || TimeEvents.Count == 0) - return new BeatInfo(-1, 0); - - var evt = TimeEvents.Where(x => time >= x.StartTime).LastOrDefault(); - if (evt == null) - evt = TimeEvents[0]; - return new BeatInfo((float)((time - evt.StartTime) / ((60000.0 / evt.BPM) * 4.0f * evt.TimeSig.Ratio) + evt.Measure)); + if (evt == null) + { + TimeEvents.Add(new Gimmick() + { + BeatInfo = new BeatInfo(Gimmicks[i].BeatInfo), + BPM = bpm.BPM, + TimeSig = new TimeSignature(Gimmicks[i].TimeSig) + }); + } + else + { + evt.TimeSig = new TimeSignature(Gimmicks[i].TimeSig); + } + timeSig = Gimmicks[i]; } + } - /// - /// Translate measures into clock time - /// - /// - /// - public int GetTime(BeatInfo beat) - { - if (TimeEvents == null || TimeEvents.Count == 0) - return 0; + // Run through all time events and generate valid start times + TimeEvents[0].StartTime = Offset * 1000.0; + for (int i = 1; i < TimeEvents.Count; i++) + { + TimeEvents[i].StartTime = ((TimeEvents[i].Measure - TimeEvents[i - 1].Measure) * (4.0f * TimeEvents[i - 1].TimeSig.Ratio * (60000.0 / TimeEvents[i - 1].BPM))) + TimeEvents[i - 1].StartTime; + } + } + /* + ((60000.0 / evt.BPM) * 4.0 * evt.TimeSig.Ratio) * measure = time + time / ((60000.0 / evt.BPM) * 4.0 * evt.TimeSig.Ratio) = measure + */ - var evt = TimeEvents.Where(x => beat.MeasureDecimal >= x.Measure).LastOrDefault(); - if (evt == null) - evt = TimeEvents[0]; - return (int)(((60000.0 / evt.BPM) * 4.0f * evt.TimeSig.Ratio) * (beat.MeasureDecimal - evt.Measure) + evt.StartTime); - } + /// + /// Translate clock time to beats + /// + /// Current timestamp in ms + /// + public BeatInfo GetBeat(float time) + { + if (TimeEvents == null || TimeEvents.Count == 0) + return new BeatInfo(-1, 0); + + var evt = TimeEvents.Where(x => time >= x.StartTime).LastOrDefault(); + if (evt == null) + evt = TimeEvents[0]; + return new BeatInfo((float)((time - evt.StartTime) / ((60000.0 / evt.BPM) * 4.0f * evt.TimeSig.Ratio) + evt.Measure)); + } + + /// + /// Translate measures into clock time + /// + /// + /// + public int GetTime(BeatInfo beat) + { + if (TimeEvents == null || TimeEvents.Count == 0) + return 0; + + var evt = TimeEvents.Where(x => beat.MeasureDecimal >= x.Measure).LastOrDefault(); + if (evt == null) + evt = TimeEvents[0]; + return (int)(((60000.0 / evt.BPM) * 4.0f * evt.TimeSig.Ratio) * (beat.MeasureDecimal - evt.Measure) + evt.StartTime); } + } } \ No newline at end of file diff --git a/BAKKA-Editor/UserSettings.cs b/BAKKA-Editor/UserSettings.cs index 08a6c72..2a19f89 100644 --- a/BAKKA-Editor/UserSettings.cs +++ b/BAKKA-Editor/UserSettings.cs @@ -8,42 +8,42 @@ namespace BAKKA_Editor { - internal class UserSettings - { - public ViewSettings ViewSettings { get; set; } = new(); - public SaveSettings SaveSettings { get; set; } = new(); - public HotkeySettings HotkeySettings { get; set; } = new(); - } + internal class UserSettings + { + public ViewSettings ViewSettings { get; set; } = new(); + public SaveSettings SaveSettings { get; set; } = new(); + public HotkeySettings HotkeySettings { get; set; } = new(); + } - internal class ViewSettings - { - public bool ShowCursor { get; set; } = true; - public bool ShowCursorDuringPlayback { get; set; } = false; - public bool HighlightViewedNote { get; set; } = true; - public bool SelectLastInsertedNote { get; set; } = true; - public bool ShowGimmicks { get; set; } = true; - public float HispeedSetting { get; set; } = 1.5f; - public int Volume { get; set; } = 100; - } + internal class ViewSettings + { + public bool ShowCursor { get; set; } = true; + public bool ShowCursorDuringPlayback { get; set; } = false; + public bool HighlightViewedNote { get; set; } = true; + public bool SelectLastInsertedNote { get; set; } = true; + public bool ShowGimmicks { get; set; } = true; + public float HispeedSetting { get; set; } = 1.5f; + public int Volume { get; set; } = 100; + } - internal class SaveSettings - { - /// - /// How frequently autosave occurs (in minutes) - /// - public int AutoSaveInterval { get; set; } = 1; - } + internal class SaveSettings + { + /// + /// How frequently autosave occurs (in minutes) + /// + public int AutoSaveInterval { get; set; } = 1; + } - internal class HotkeySettings - { - private static readonly CultureInfo _defaultParsingCulture = CultureInfo.InvariantCulture; - public int TouchHotkey { get; set; } = Convert.ToInt32(Keys.D1, _defaultParsingCulture); - public int SlideLeftHotkey { get; set; } = Convert.ToInt32(Keys.D2, _defaultParsingCulture); - public int SlideRightHotkey { get; set; } = Convert.ToInt32(Keys.D3, _defaultParsingCulture); - public int SnapUpHotkey { get; set; } = Convert.ToInt32(Keys.D4, _defaultParsingCulture); - public int SnapDownHotkey { get; set; } = Convert.ToInt32(Keys.D5, _defaultParsingCulture); - public int ChainHotkey { get; set; } = Convert.ToInt32(Keys.D6, _defaultParsingCulture); - public int HoldHotkey { get; set; } = Convert.ToInt32(Keys.D7, _defaultParsingCulture); - public int PlayHotkey { get; set; } = Convert.ToInt32(Keys.Space, _defaultParsingCulture); - } + internal class HotkeySettings + { + private static readonly CultureInfo _defaultParsingCulture = CultureInfo.InvariantCulture; + public int TouchHotkey { get; set; } = Convert.ToInt32(Keys.D1, _defaultParsingCulture); + public int SlideLeftHotkey { get; set; } = Convert.ToInt32(Keys.D2, _defaultParsingCulture); + public int SlideRightHotkey { get; set; } = Convert.ToInt32(Keys.D3, _defaultParsingCulture); + public int SnapUpHotkey { get; set; } = Convert.ToInt32(Keys.D4, _defaultParsingCulture); + public int SnapDownHotkey { get; set; } = Convert.ToInt32(Keys.D5, _defaultParsingCulture); + public int ChainHotkey { get; set; } = Convert.ToInt32(Keys.D6, _defaultParsingCulture); + public int HoldHotkey { get; set; } = Convert.ToInt32(Keys.D7, _defaultParsingCulture); + public int PlayHotkey { get; set; } = Convert.ToInt32(Keys.Space, _defaultParsingCulture); + } }