226 lines
6.8 KiB
C#
226 lines
6.8 KiB
C#
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<Resource> ConvertFrom(FileInfo file) {
|
|
List<Resource> result = new List<Resource>();
|
|
MalodyChart src;
|
|
if (file.Extension != ".mc") throw new NotImplementedException("mcz file is not supported");
|
|
using (var reader = new StreamReader(file.FullName)) {
|
|
src = JsonConvert.DeserializeObject<MalodyChart>(reader.ReadToEnd());
|
|
}
|
|
if (src.meta.mode != 0) throw new NotImplementedException("The chart mode is not supported");
|
|
|
|
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<Chart.Signature>(),
|
|
sounds = new List<Chart.Sound>(),
|
|
motions = new List<Chart.Motion>(),
|
|
groups = new List<Chart.Group>(),
|
|
};
|
|
var group = new Chart.Group() {
|
|
tracks = new List<Chart.Track>(),
|
|
notes = new List<Chart.Note>(),
|
|
motions = new List<Chart.Motion>(),
|
|
};
|
|
chart.groups.Add(group);
|
|
int col = src.meta.mode_ext.column;
|
|
|
|
IEnumerable<MalodyChart.IEvent> events = src.time.Cast<MalodyChart.IEvent>();
|
|
if (src.effect != null) events = events.Concat(src.effect.Cast<MalodyChart.IEvent>());
|
|
events = events.Concat(src.note.Cast<MalodyChart.IEvent>());
|
|
List<MalodyChart.IEvent> endEvents = new List<MalodyChart.IEvent>();
|
|
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<MalodyChart.IEvent, StartEventState> longEvents
|
|
= new Dictionary<MalodyChart.IEvent, StartEventState>();
|
|
|
|
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();
|
|
}
|
|
else {
|
|
var rn = new Chart.Note() {
|
|
time = beat,
|
|
motions = new List<Chart.Motion> {
|
|
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();
|
|
}
|
|
else throw new NotSupportedException();
|
|
}
|
|
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<Time> time;
|
|
public struct Time : IEvent {
|
|
public int[] beat { get; set; }
|
|
public int[] endbeat { get; set; }
|
|
public float bpm;
|
|
public int Priority { get { return -2; } }
|
|
}
|
|
|
|
public List<Effect> effect;
|
|
public struct Effect : IEvent {
|
|
public int[] beat { get; set; }
|
|
public int[] endbeat { get; set; }
|
|
public float? scroll;
|
|
public int Priority { get { return 0; } }
|
|
}
|
|
|
|
public List<Note> note;
|
|
public struct Note : IEvent {
|
|
public int[] beat { get; set; }
|
|
public int[] endbeat { get; set; }
|
|
public int column;
|
|
public string sound;
|
|
public int offset;
|
|
public int type;
|
|
public int Priority { get { return 0; } }
|
|
}
|
|
}
|
|
#pragma warning restore IDE1006
|
|
}
|
|
}
|