using Cryville.Crtr.Browsing; using Quaver.API.Maps; using Quaver.API.Maps.Structures; using System; using System.Collections.Generic; using System.Globalization; using System.IO; namespace Cryville.Crtr.Extensions.Quaver { public class QuaverChartConverter : ResourceConverter { static readonly string[] SUPPORTED_FORMATS = { ".qua" }; const double OFFSET = 0.05; public override string[] GetSupportedFormats() { return SUPPORTED_FORMATS; } public override IEnumerable ConvertFrom(FileInfo file) { List result = new List(); var src = Qua.Parse(file.FullName); var ruleset = "quaver!" + src.Mode.ToString().ToLower(); var meta = new ChartMeta { name = src.DifficultyName, author = src.Creator, song = new SongMetaInfo { name = src.Title, author = src.Artist, }, ruleset = ruleset, cover = src.BackgroundFile, note_count = src.HitObjects.Count, }; var chart = new Chart { format = 2, time = new BeatTime(-4, 0, 1), ruleset = ruleset, sigs = new List(), sounds = new List { new Chart.Sound { time = new BeatTime(0, 0, 1), id = src.Title, offset = (float)(src.TimingPoints[0].StartTime / 1e3 + OFFSET) } }, motions = new List(), groups = new List(), }; var group = new Chart.Group() { tracks = new List(), notes = new List(), motions = new List(), }; chart.groups.Add(group); result.Add(new RawChartResource(string.Format("{0} - {1}", meta.song.name, meta.name), chart, meta)); result.Add(new SongResource(meta.song.name, new FileInfo(Path.Combine(file.DirectoryName, src.AudioFile)))); var evs = new List(); foreach (var e in src.HitObjects) evs.Add(new EventWrapper.HitObject(e)); foreach (var e in src.SliderVelocities) evs.Add(new EventWrapper.SliderVelocity(e)); foreach (var e in src.SoundEffects) evs.Add(new EventWrapper.SoundEffect(e)); foreach (var e in src.TimingPoints) evs.Add(new EventWrapper.TimingPoint(e)); var evc = evs.Count; for (int i = 0; i < evc; i++) if (evs[i].IsLong) evs.Add(new EventWrapper.EndEvent(evs[i])); evs.Sort(); var longevs = new Dictionary(); var tm = new TimeTimingModel(src.TimingPoints[0].StartTime / 1e3); foreach (var ev in evs) { tm.ForwardTo(ev.StartTime / 1e3); if (ev is EventWrapper.HitObject) { var tev = (EventWrapper.HitObject)ev; var rn = new Chart.Note { time = tm.FractionalBeatTime, motions = new List { new Chart.Motion { motion = string.Format(CultureInfo.InvariantCulture, "track:{0}", tev.Event.Lane - 1) } }, }; if (ev.IsLong) longevs.Add(ev, rn); group.notes.Add(rn); } else if (ev is EventWrapper.SliderVelocity) { var tev = (EventWrapper.SliderVelocity)ev; group.motions.Add(new Chart.Motion { time = tm.FractionalBeatTime, motion = string.Format(CultureInfo.InvariantCulture, "svm:{0}", tev.Event.Multiplier), }); } else if (ev is EventWrapper.TimingPoint) { var tev = (EventWrapper.TimingPoint)ev; tm.BPM = tev.Event.Bpm; chart.sigs.Add(new Chart.Signature { time = tm.FractionalBeatTime, tempo = tev.Event.Bpm, }); } else if (ev is EventWrapper.EndEvent) { var tev = (EventWrapper.EndEvent)ev; var oev = tev.Original; longevs[oev].endtime = tm.FractionalBeatTime; } else throw new NotSupportedException("Sound effects are not supported yet"); } var endbeat = tm.FractionalBeatTime; endbeat.b += 4; chart.endtime = endbeat; meta.length = (float)tm.Time; return result; } abstract class EventWrapper : IComparable { public abstract int StartTime { get; } public abstract int EndTime { get; } public bool IsLong { get { return EndTime > 0; } } public abstract int Priority { get; } public int CompareTo(EventWrapper other) { var c = StartTime.CompareTo(other.StartTime); if (c != 0) return c; return Priority.CompareTo(other.Priority); } public class HitObject : EventWrapper { public HitObjectInfo Event; public HitObject(HitObjectInfo ev) { Event = ev; } public override int StartTime { get { return Event.StartTime; } } public override int EndTime { get { return Event.EndTime; } } public override int Priority { get { return 0; } } } public class SliderVelocity : EventWrapper { public SliderVelocityInfo Event; public SliderVelocity(SliderVelocityInfo ev) { Event = ev; } public override int StartTime { get { return (int)Event.StartTime; } } public override int EndTime { get { return 0; } } public override int Priority { get { return 0; } } } public class SoundEffect : EventWrapper { public SoundEffectInfo Event; public SoundEffect(SoundEffectInfo ev) { Event = ev; } public override int StartTime { get { return (int)Event.StartTime; } } public override int EndTime { get { return 0; } } public override int Priority { get { return 0; } } } public class TimingPoint : EventWrapper { public TimingPointInfo Event; public TimingPoint(TimingPointInfo ev) { Event = ev; } public override int StartTime { get { return (int)Event.StartTime; } } public override int EndTime { get { return 0; } } public override int Priority { get { return -2; } } } public class EndEvent : EventWrapper { public EventWrapper Original; public EndEvent(EventWrapper ev) { if (!ev.IsLong) throw new ArgumentException("Event is not long"); Original = ev; } public override int StartTime { get { return Original.EndTime; } } public override int EndTime { get { return 0; } } public override int Priority { get { return Original.Priority - 1; } } } } } }