using Cryville.Crtr.Browsing; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; namespace Cryville.Crtr.Extensions.Malody { public class MalodyChartConverter : ResourceConverter { static readonly string[] SUPPORTED_FORMATS = { ".mc", ".mcz" }; static readonly string[] MODES = { "key", "step", "dj", "catch", "pad", "taiko", "ring", "slide", "live" }; public override string[] GetSupportedFormats() { return SUPPORTED_FORMATS; } public override IEnumerable ConvertFrom(FileInfo file) { List result = new List(); MalodyChart src; if (file.Extension != ".mc") throw new NotImplementedException("mcz file is not supported yet"); using (var reader = new StreamReader(file.FullName)) { src = JsonConvert.DeserializeObject(reader.ReadToEnd()); } if (src.meta.mode != 0) throw new NotImplementedException(string.Format("{0} mode is not supported yet", MODES[src.meta.mode])); var ruleset = "malody!" + MODES[src.meta.mode]; if (src.meta.mode == 0) { ruleset += "." + src.meta.mode_ext.column.ToString(CultureInfo.InvariantCulture) + "k"; } ChartMeta meta = new ChartMeta() { name = src.meta.version, author = src.meta.creator, song = new SongMetaInfo() { name = src.meta.song.titleorg != null ? src.meta.song.titleorg : src.meta.song.title, author = src.meta.song.artistorg != null ? src.meta.song.artistorg : src.meta.song.artist, }, ruleset = ruleset, }; Chart chart = new Chart { format = 2, time = new BeatTime(-4, 0, 1), ruleset = ruleset, sigs = new List(), sounds = new List(), motions = new List(), groups = new List(), }; var group = new Chart.Group() { tracks = new List(), notes = new List(), motions = new List(), }; chart.groups.Add(group); int col = src.meta.mode_ext.column; IEnumerable events = src.time.Cast(); if (src.effect != null) events = events.Concat(src.effect.Cast()); events = events.Concat(src.note.Cast()); List endEvents = new List(); foreach (var ev in events) if (ev.endbeat != null) endEvents.Add(new MalodyChart.EndEvent { beat = ev.endbeat, StartEvent = ev, }); events = events.Concat(endEvents) .OrderBy(e => e.beat[0] + (float)e.beat[1] / e.beat[2]) .ThenBy(e => e.Priority); Dictionary longEvents = new Dictionary(); float? baseBpm = null; var tm = new FractionalBeatTimeTimingModel(); foreach (var ev in events) { var beat = new BeatTime(ev.beat[0], ev.beat[1], ev.beat[2]); tm.ForwardTo(beat); if (ev is MalodyChart.Time) { var tev = (MalodyChart.Time)ev; if (baseBpm == null) baseBpm = tev.bpm; tm.BPM = tev.bpm; chart.sigs.Add(new Chart.Signature { time = beat, tempo = tev.bpm, }); chart.motions.Add(new Chart.Motion { time = beat, motion = "svm:" + (tev.bpm / baseBpm.Value).ToString(CultureInfo.InvariantCulture) }); } else if (ev is MalodyChart.Effect) { var tev = (MalodyChart.Effect)ev; if (tev.scroll != null) group.motions.Add(new Chart.Motion { time = beat, motion = "svm:" + tev.scroll.Value.ToString(CultureInfo.InvariantCulture) }); } else if (ev is MalodyChart.Note) { var tev = (MalodyChart.Note)ev; if (tev.type == 1) { if (tev.beat[0] == 0 && tev.beat[1] == 0) { var res = new SongResource(meta.song.name, new FileInfo(file.DirectoryName + "/" + tev.sound)); result.Add(res); chart.sounds.Add(new Chart.Sound { time = new BeatTime(0, 0, 1), id = res.Name, offset = -tev.offset / 1000f, }); } else throw new NotImplementedException("Key sounds are not supported yet"); } else { var rn = new Chart.Note() { time = beat, motions = new List { new Chart.Motion() { motion = "track:" + tev.column.ToString(CultureInfo.InvariantCulture) } }, }; if (tev.endbeat != null) { rn.endtime = new BeatTime(tev.endbeat[0], tev.endbeat[1], tev.endbeat[2]); longEvents.Add(ev, new StartEventState { Destination = rn, Time = tm.Time, }); } group.notes.Add(rn); } } else if (ev is MalodyChart.EndEvent) { var tev = (MalodyChart.EndEvent)ev; if (tev.StartEvent is MalodyChart.Note) { var sev = tev.StartEvent; longEvents.Remove(sev); } else throw new NotSupportedException("Unrecognized long event"); } else throw new NotSupportedException("Unrecognized event"); } var endbeat = tm.FractionalBeatTime; endbeat.b += 4; chart.endtime = endbeat; meta.length = (float)tm.Time; meta.note_count = group.notes.Count; string chartName = string.Format("{0} - {1}", meta.song.name, meta.name); if (src.meta.background != null) { meta.cover = src.meta.background; } result.Add(new RawChartResource(chartName, chart, meta)); return result; } struct StartEventState { public double Time { get; set; } public ChartEvent Destination { get; set; } } #pragma warning disable IDE1006 struct MalodyChart { public interface IEvent { int[] beat { get; set; } int[] endbeat { get; set; } int Priority { get; } } public struct EndEvent : IEvent { public int[] beat { get; set; } public int[] endbeat { get; set; } public IEvent StartEvent { get; set; } public int Priority { get { return StartEvent.Priority - 1; } } } public Meta meta; public struct Meta { public SongInfo song; public struct SongInfo { public string title; public string artist; public string titleorg; public string artistorg; } public string background; public string creator; public string version; public int mode; public ModeExt mode_ext; public struct ModeExt { public int column; } } public List