Created
August 3, 2018 15:27
-
-
Save TigerHix/edf9933ce98da7ce92b9a70364ba4935 to your computer and use it in GitHub Desktop.
Converts a given Cytus chart to Cytus II formatted chart.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using Newtonsoft.Json; | |
using Newtonsoft.Json.Linq; | |
namespace TigerHix | |
{ | |
class CytusChartConverter | |
{ | |
public static void Fain(string[] args) | |
{ | |
var chart = new Chart(File.ReadAllText(args[0])); | |
const int timeBase = 480; | |
var root = new JObject(); | |
root["format_version"] = 0; | |
root["time_base"] = 480; | |
root["start_offset_time"] = 0; | |
var tempo = new JObject(); | |
tempo["tick"] = 0; | |
var tempoValue = chart.PageDuration * 1000000; | |
tempo["value"] = tempoValue; | |
root["tempo_list"] = new JArray { tempo }; | |
if (chart.PageShift < 0) chart.PageShift = chart.PageShift + 2 * chart.PageDuration; | |
var pageShiftTickOffset = chart.PageShift / chart.PageDuration * timeBase; | |
var noteList = new JArray(); | |
var page = 0; | |
foreach (var note in chart.Notes.Values) | |
{ | |
var obj = new JObject(); | |
obj["id"] = note.Id; | |
switch (note.Type) | |
{ | |
case Chart.NoteType.Single: | |
obj["type"] = 0; | |
break; | |
case Chart.NoteType.Chain: | |
obj["type"] = note.IsChainHead ? 3 : 4; | |
break; | |
case Chart.NoteType.Hold: | |
obj["type"] = 1; | |
break; | |
} | |
obj["x"] = note.X; | |
var ti = note.Time * timeBase * 1000000 / tempoValue + pageShiftTickOffset; | |
obj["tick"] = ti; | |
obj["hold_tick"] = note.Duration * timeBase * 1000000 / tempoValue; | |
page = (int) Math.Floor((double) ti / timeBase); | |
obj["page_index"] = page; | |
if (note.Type == Chart.NoteType.Chain) | |
{ | |
obj["next_id"] = note.ConnectedNote != null ? note.ConnectedNote.Id : -1; | |
} | |
else | |
{ | |
obj["next_id"] = 0; | |
} | |
noteList.Add(obj); | |
} | |
root["note_list"] = noteList; | |
var pageList = new JArray(); | |
var direction = false; | |
var t = 0; | |
for (var i = 0; i <= page; i++) | |
{ | |
var obj = new JObject(); | |
obj["scan_line_direction"] = direction ? 1 : -1; | |
direction = !direction; | |
obj["start_tick"] = t; | |
t += timeBase; | |
obj["end_tick"] = t; | |
pageList.Add(obj); | |
} | |
root["page_list"] = pageList; | |
root["music_offset"] = pageShiftTickOffset / timeBase / 1000000 * tempoValue; | |
File.WriteAllText(Path.GetFileName(args[0]) + ".converted.txt", JsonConvert.SerializeObject(root)); | |
} | |
} | |
public class Chart | |
{ | |
public float PageDuration; | |
public float PageShift; | |
public Dictionary<int, Note> Notes = new Dictionary<int, Note>(); | |
public List<int> ChronologicalIds; | |
public Chart(string text) { | |
var notes = new Dictionary<int, Note>(); | |
foreach (var line in text.Split('\n')) | |
{ | |
var data = line.Split((char[]) null, StringSplitOptions.RemoveEmptyEntries); | |
if (data.Length == 0) continue; | |
var type = data[0]; | |
switch (type) | |
{ | |
case "PAGE_SIZE": | |
PageDuration = float.Parse(data[1]); | |
break; | |
case "PAGE_SHIFT": | |
PageShift = float.Parse(data[1]); | |
break; | |
case "NOTE": | |
var note = new Note(int.Parse(data[1]), float.Parse(data[2]), float.Parse(data[3]), | |
float.Parse(data[4]), false); | |
notes.Add(int.Parse(data[1]), note); | |
if (note.Duration > 0) note.Type = NoteType.Hold; | |
break; | |
case "LINK": | |
var notesInChain = new List<Note>(); | |
for (var i = 1; i < data.Length; i++) | |
{ | |
int id; | |
if (!int.TryParse(data[i], out id)) continue; | |
note = notes[id]; | |
note.Type = NoteType.Chain; | |
notesInChain.Add(note); | |
} | |
for (var i = 0; i < notesInChain.Count - 1; i++) | |
{ | |
notesInChain[i].ConnectedNote = notesInChain[i + 1]; | |
} | |
notesInChain[0].IsChainHead = true; | |
break; | |
} | |
} | |
PageShift += PageDuration; | |
// Calculate chronological note ids | |
var noteList = notes.Values.ToList(); | |
noteList.Sort((a, b) => a.Time.CompareTo(b.Time)); | |
ChronologicalIds = noteList.Select(note => note.OriginalId).ToList(); | |
// Recalculate note ids from original ids | |
var newId = 0; | |
foreach (var noteId in ChronologicalIds) | |
{ | |
notes[noteId].Id = newId; | |
Notes[newId] = notes[noteId]; | |
newId++; | |
} | |
// Reset chronological ids | |
ChronologicalIds.Clear(); | |
for (var i = 0; i < notes.Count; i++) | |
{ | |
ChronologicalIds.Add(i); | |
} | |
} | |
public class Note | |
{ | |
public int Id; | |
public int OriginalId; | |
public float Time; | |
public float X; | |
public float Duration; | |
public NoteType Type = NoteType.Single; | |
public Note ConnectedNote; | |
public bool IsChainHead; | |
public Note(int originalId, float time, float x, float duration, bool isChainHead) | |
{ | |
OriginalId = originalId; | |
Time = time; | |
X = x; | |
Duration = duration; | |
IsChainHead = isChainHead; | |
} | |
} | |
public enum NoteType | |
{ | |
Single, Chain, Hold | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment