public void ParseBlock(IngameBasis Game, ref int ID, ref double Time, ref DelesteGlobalData StaticData) { int MaxBit = 1; List <int> DataIndex = new List <int>(), StartIndex = new List <int>(), EndIndex = new List <int>(); List <string[]> Datas = new List <string[]>(); // DataIndex, StartIndex, EndIndex의 초기화와 MaxBit의 갱신, 그리고 Datas의 값 대입을 수행합니다. for (int i = 0; i < DataLines.Count; i++) { Datas.Add(DataLines[i].Split(new char[] { ':' })); DataIndex.Add(-1); StartIndex.Add(0); EndIndex.Add(0); MaxBit = LCM(MaxBit, Datas[i][1].Length); } // MaxBit를 기반으로, 차례차례 데이터를 해석하고 추가하는 전체 과정입니다. for (int i = 0; i < MaxBit; i++) { // 선제적으로, 각종 큐들을 살펴보면서 조건을 만족한다면 데이터를 그에 맞춥니다. if (StaticData.Measure.Count > 0 && StaticData.MeasurePos[0] <= Measure + (i / (double)MaxBit)) { StaticData.BeatMultiplier = StaticData.Measure[0]; StaticData.Measure.RemoveAt(0); StaticData.MeasurePos.RemoveAt(0); } if (StaticData.ChangeBPM.Count > 0 && StaticData.ChangeBPMPos[0] <= Measure + (i / (double)MaxBit)) { StaticData.CurrentBPM = StaticData.ChangeBPM[0]; StaticData.ChangeBPM.RemoveAt(0); StaticData.ChangeBPMPos.RemoveAt(0); } if (StaticData.HS2.Count > 0 && StaticData.HS2Pos[0] <= Measure + (i / (double)MaxBit)) { StaticData.SpeedMultiplier = StaticData.HS2[0]; StaticData.HS2.RemoveAt(0); StaticData.HS2Pos.RemoveAt(0); } if (StaticData.Delay.Count > 0 && StaticData.DelayPos[0] <= Measure + (i / (double)MaxBit)) { Time += StaticData.Delay[0]; StaticData.Delay.RemoveAt(0); StaticData.DelayPos.RemoveAt(0); } if (StaticData.Scroll.Count > 0 && StaticData.ScrollPos[0] <= Measure + (i / (double)MaxBit)) { Game.CreateNote(new NoteData(ID++, 0, (float)Time, (float)StaticData.Scroll[0][0], 0, 0, NoteType.Scroller, FlickType.NotFlick, new Color32(255, 255, 255, 0), new List <int>())); StaticData.EndScrollTime = Time + StaticData.Scroll[0][1]; StaticData.IsScrollModified = true; StaticData.Scroll.RemoveAt(0); StaticData.ScrollPos.RemoveAt(0); } if (StaticData.EndScrollTime <= Time && StaticData.IsScrollModified) { Game.CreateNote(new NoteData(ID++, 0, (float)Time, 1, 0, 0, NoteType.Scroller, FlickType.NotFlick, new Color32(255, 255, 255, 0), new List <int>())); StaticData.IsScrollModified = false; } // Data를 병렬적으로 처리합니다. for (int j = 0; j < Datas.Count; j++) { // 만약 현재의 i 지점에 해당하는 해당 데이터의 계산된 인덱스가 기존의 인덱스보다 크다면 값을 그에 맞춥니다. if (i / (MaxBit / Datas[j][1].Length) > DataIndex[j]) { DataIndex[j]++; } else { continue; } // 데이터를 가져옵니다. 코드 간략화를 위함입니다. char Command = Datas[j][1][DataIndex[j]]; // 필요한 개별 변수를 설정합니다. (1차) NoteType Mode; FlickType Flick; // 노트의 기본 정보를 해석합니다. if (Command.Equals('1') || char.ToUpper(Command).Equals('L')) { Mode = NoteType.Tap; Flick = FlickType.Left; } else if (Command.Equals('2') || char.ToUpper(Command).Equals('T')) { Mode = NoteType.Tap; Flick = FlickType.NotFlick; } else if (Command.Equals('3') || char.ToUpper(Command).Equals('R')) { Mode = NoteType.Tap; Flick = FlickType.Right; } else if (Command.Equals('4') || char.ToUpper(Command).Equals('H')) { Mode = NoteType.HoldStart; Flick = FlickType.NotFlick; } else if (Command.Equals('5') || char.ToUpper(Command).Equals('S')) { Mode = NoteType.SlideStart; Flick = FlickType.NotFlick; } else { continue; } // 필요한 개별 변수를 설정합니다. (2차) double Start = 3, End = 3; // 시작 지점과 끝 지점을 설정합니다. // 위 코드의 continue 때문에, 유효한 노트 데이터가 해석이 되어야 이 부분부터의 코드에 도달합니다. if (Datas[j].Length >= 3) { int DummyStart; char StartText = Datas[j][2][StartIndex[j]]; if (int.TryParse(StartText.ToString(), out DummyStart)) { Start = DummyStart; } else { if (char.ToUpper(StartText).Equals('A')) { Start = 1.5; } else if (char.ToUpper(StartText).Equals('B')) { Start = 2.5; } else if (char.ToUpper(StartText).Equals('C')) { Start = 3.5; } else if (char.ToUpper(StartText).Equals('D')) { Start = 4.5; } else { throw new Exception("While parsing Deleste file: Error in start point parsing."); } } StartIndex[j]++; if (Datas[j].Length.Equals(3)) { End = Start; } } if (Datas[j].Length >= 4) { int DummyEnd; char EndText = Datas[j][3][EndIndex[j]]; if (int.TryParse(EndText.ToString(), out DummyEnd)) { End = DummyEnd; } else { if (char.ToUpper(EndText).Equals('A')) { End = 1.5; } else if (char.ToUpper(EndText).Equals('B')) { End = 2.5; } else if (char.ToUpper(EndText).Equals('C')) { End = 3.5; } else if (char.ToUpper(EndText).Equals('D')) { End = 4.5; } else { throw new Exception("While parsing Deleste file: Error in end point parsing."); } } EndIndex[j]++; } // 필요한 개별 변수를 설정합니다. (3차) List <int> Prevs = new List <int>(); if (!StaticData.HoldPrev.ContainsKey(End)) { StaticData.HoldPrev.Add(End, 0); } if (!StaticData.TailPrev.ContainsKey(Channel[j])) { StaticData.TailPrev.Add(Channel[j], 0); } if (!StaticData.ConnectorPrev.ContainsKey(Channel[j])) { StaticData.ConnectorPrev.Add(Channel[j], new List <double[]>(3)); } if (!StaticData.BeforeTime.ContainsKey(Channel[j])) { StaticData.BeforeTime.Add(Channel[j], 0); } // 연결할 이전 노트가 있으면 연결합니다. Tail, Connector 순서로 연결합니다. // Tail은 홀드 노트를 우선으로 합니다 (추후 변경 가능). 중복은 허용하지 않습니다. if (Mode.Equals(NoteType.Tap) && StaticData.HoldPrev[End] > 0) { Mode = NoteType.HoldEnd; Prevs.Add(StaticData.HoldPrev[End]); StaticData.HoldPrev[End] = 0; } else if (Mode.Equals(NoteType.Tap) && StaticData.TailPrev[Channel[j]] > 0) { Mode = NoteType.SlideEnd; Prevs.Add(StaticData.TailPrev[Channel[j]]); StaticData.TailPrev[Channel[j]] = 0; } if (Mode.Equals(NoteType.SlideStart) && StaticData.TailPrev[Channel[j]] > 0) { Mode = NoteType.SlideMiddle; Prevs.Add(StaticData.TailPrev[Channel[j]]); } // Connector를 연결합니다. // ConnectorPrev[Channel[j]][x]에서 x=0의 값은 방향(증가 방향이 양수), x=1의 값은 비교 지점, x=2의 값은 노트 ID입니다. if (!Flick.Equals(FlickType.NotFlick) && StaticData.ConnectorPrev[Channel[j]].Count > 0) { for (int k = 0; k < StaticData.ConnectorPrev[Channel[j]].Count; k++) { if (Time - StaticData.BeforeTime[Channel[j]] > 0 && Time - StaticData.BeforeTime[Channel[j]] <= 1) { if (StaticData.ConnectorPrev[Channel[j]][k][0] > 0) { if (End > StaticData.ConnectorPrev[Channel[j]][k][1]) { Prevs.Add((int)StaticData.ConnectorPrev[Channel[j]][k][2]); } } else if (StaticData.ConnectorPrev[Channel[j]][k][0] < 0) { if (End < StaticData.ConnectorPrev[Channel[j]][k][1]) { Prevs.Add((int)StaticData.ConnectorPrev[Channel[j]][k][2]); } } else { Prevs.Add((int)StaticData.ConnectorPrev[Channel[j]][k][2]); } } } if (StaticData.BeforeTime[Channel[j]] != Time) { StaticData.ConnectorPrev[Channel[j]].Clear(); } } else if (Flick.Equals(FlickType.NotFlick) && StaticData.ConnectorPrev[Channel[j]].Count > 0 && StaticData.BeforeTime[Channel[j]] != Time) { StaticData.ConnectorPrev[Channel[j]].Clear(); } // 다음 노트와 연결될 노트에 대한 데이터를 전역 데이터에 입력합니다. if (Mode.Equals(NoteType.HoldStart)) { StaticData.HoldPrev[End] = ID; } else if (Mode.Equals(NoteType.SlideStart) || Mode.Equals(NoteType.SlideMiddle)) { StaticData.TailPrev[Channel[j]] = ID; } if (!Flick.Equals(FlickType.NotFlick)) { if (Channel[j] % 4 == 0 || Channel[j] % 4 == 1) { if (Flick.Equals(FlickType.Left)) { StaticData.ConnectorPrev[Channel[j]].Add(new double[3] { -1, End, ID }); } else if (Flick.Equals(FlickType.Right)) { StaticData.ConnectorPrev[Channel[j]].Add(new double[3] { 1, End, ID }); } } else if (Channel[j] % 4 == 2 || Channel[j] % 4 == 3) { StaticData.ConnectorPrev[Channel[j]].Add(new double[3] { 0, End, ID }); } StaticData.BeforeTime[Channel[j]] = Time; } // 노트를 게임에 추가합니다. Game.CreateNote(new NoteData(ID++, 1, (float)Time, (float)(Speed[j] * StaticData.SpeedMultiplier), (float)Start, (float)End, Mode, Flick, new Color32(Color[j][0], Color[j][1], Color[j][2], Color[j][3]), Prevs)); } Time += (((240 / StaticData.CurrentBPM) * StaticData.BeatMultiplier) / MaxBit); } }
public void ParseDeleste(string path, out bool succeed) { FileStream file = null; StreamReader scanner = null; try { file = new FileStream(path, FileMode.Open, FileAccess.Read); scanner = new StreamReader(file, System.Text.Encoding.UTF8); } catch (Exception e) { TSystemStatic.LogWithException("Beatmap file does not exists.", e); succeed = false; return; } int d, MaxBlockNum = -1, ID = 1; char[] div = { ' ', ',', ':' }; DelesteGlobalData StaticData = new DelesteGlobalData(); Dictionary <int, DelesteBlockData> Blocks = new Dictionary <int, DelesteBlockData>(); double Time = 0, Speed = 1.0; byte[] Color = new byte[] { 255, 255, 255, 255 }; var SongTime = TSystemConfig.Now.gameSync; // Steadily, stacks the data. while (scanner.Peek() != -1) { try { string dataLine = scanner.ReadLine(); string[] data = dataLine.Split(div, StringSplitOptions.RemoveEmptyEntries); if (data.Length > 1 && data[0].StartsWith("#") && (data[0].Substring(1).ToUpper().Equals("BPM") || data[0].Substring(1).ToUpper().Equals("TEMPO"))) { StaticData.CurrentBPM = double.Parse(data[1]); } else if (data.Length > 1 && data[0].StartsWith("#") && data[0].Substring(1).ToUpper().Equals("OFFSET")) { Time += (int.Parse(data[1]) / 1000d); } else if (data.Length > 1 && data[0].StartsWith("#") && (data[0].Substring(1).ToUpper().Equals("SONGOFFSET") || data[0].Substring(1).ToUpper().Equals("MUSICOFFSET") || data[0].Substring(1).ToUpper().Equals("BGMOFFSET"))) { Time -= (int.Parse(data[1]) / 1000d); } else if (data.Length > 1 && data[0].StartsWith("#") && data[0].Substring(1).ToUpper().Equals("ATTRIBUTE")) { if (data[1].ToUpper().Equals("CUTE") || data[1].ToUpper().Equals("CU") || data[1].Equals("1")) { Color = new byte[] { 255, 100, 200, 255 }; } else if (data[1].ToUpper().Equals("COOL") || data[1].ToUpper().Equals("CO") || data[1].Equals("2")) { Color = new byte[] { 85, 135, 255, 255 }; } else if (data[1].ToUpper().Equals("PASSION") || data[1].ToUpper().Equals("PA") || data[1].Equals("3")) { Color = new byte[] { 255, 220, 50, 255 }; } else if (data[1].ToUpper().Equals("ALL") || data[1].Equals("4")) { Color = new byte[] { 230, 255, 255, 255 }; } } else if (data.Length > 2 && data[0].StartsWith("#") && (data[0].Substring(1).ToUpper().Equals("MEASURE") || data[0].Substring(1).ToUpper().Equals("MEAS") || data[0].Substring(1).ToUpper().Equals("MEA"))) { if (data[2].Contains("/")) { string[] numbers = data[2].Split(new char[] { '/' }); StaticData.Measure.Add(double.Parse(numbers[0]) / double.Parse(numbers[1])); } else { StaticData.Measure.Add(double.Parse(data[2])); } StaticData.MeasurePos.Add(double.Parse(data[1])); } else if (data.Length > 2 && (data[0].StartsWith("#") && (data[0].Substring(1).ToUpper().Equals("CHANGEBPM") || data[0].Substring(1).ToUpper().Equals("CHANGETEMPO")))) { StaticData.ChangeBPM.Add(double.Parse(data[2])); StaticData.ChangeBPMPos.Add(double.Parse(data[1])); } else if (data.Length > 1 && data[0].StartsWith("#") && data[0].Substring(1).ToUpper().Equals("CHANGEATTRIBUTE")) { if (data[1].ToUpper().Equals("CUTE") || data[1].ToUpper().Equals("CU") || data[1].Equals("1")) { Color = new byte[] { 255, 100, 200, 255 }; } else if (data[1].ToUpper().Equals("COOL") || data[1].ToUpper().Equals("CO") || data[1].Equals("2")) { Color = new byte[] { 85, 135, 255, 255 }; } else if (data[1].ToUpper().Equals("PASSION") || data[1].ToUpper().Equals("PA") || data[1].Equals("3")) { Color = new byte[] { 255, 220, 50, 255 }; } else if (data[1].ToUpper().Equals("ALL") || data[1].Equals("4")) { Color = new byte[] { 230, 255, 255, 255 }; } } else if (data.Length > 1 && data[0].StartsWith("#") && (data[0].Substring(1).ToUpper().Equals("HISPEED") || data[0].Substring(1).ToUpper().Equals("HS"))) { Speed = double.Parse(data[1]); if (Speed <= 0) { throw new Exception("TSystem does not support HiSpeed zero and below."); } } else if (data.Length > 2 && data[0].StartsWith("#") && data[0].Substring(1).ToUpper().Equals("HS2")) { //if (double.Parse(data[2]) <= 0) // throw new Exception("TSystem does not support HiSpeed zero and below.")); //StaticData.HS2.Add(double.Parse(data[2])); //StaticData.HS2Pos.Add(double.Parse(data[1])); TSystemStatic.Log("While parsing Deleste file: TSystem does not support HS2."); } else if (data.Length > 2 && data[0].StartsWith("#") && data[0].Substring(1).ToUpper().Equals("DELAY")) { StaticData.Delay.Add(double.Parse(data[2])); StaticData.DelayPos.Add(double.Parse(data[1])); } else if (data.Length > 3 && data[0].StartsWith("#") && data[0].Substring(1).ToUpper().Equals("SCROLL")) { StaticData.Scroll.Add(new double[2] { double.Parse(data[2]) / 1000d, double.Parse(data[3]) / 1000d }); StaticData.ScrollPos.Add(double.Parse(data[1])); } else if (data.Length > 0 && data[0].StartsWith("#") && int.TryParse(data[0].Substring(1, 1), out d)) { int CurBlock = int.Parse(data[1]); if (!Blocks.ContainsKey(CurBlock)) { Blocks.Add(CurBlock, new DelesteBlockData(CurBlock)); } Blocks[CurBlock].DataLines.Add(dataLine); Blocks[CurBlock].Channel.Add(int.Parse(data[0].Substring(1))); Blocks[CurBlock].Color.Add(Color); Blocks[CurBlock].Speed.Add(Speed); if (MaxBlockNum < CurBlock) { MaxBlockNum = CurBlock; } } } catch (Exception e) { TSystemStatic.LogWithException("Failed to parse Deleste type beatmap.", e); scanner.Close(); file.Close(); succeed = false; return; } } scanner.Close(); file.Close(); // Creates note at here. Game.CreateNote(new NoteData(0, 0, SongTime, (float)Speed, 0, 0, NoteType.Starter, FlickType.NotFlick, new Color32(255, 255, 255, 255), new List <int>())); for (int i = 0; i <= MaxBlockNum; i++) { if (Blocks.ContainsKey(i)) { Blocks[i].ParseBlock(Game, ref ID, ref Time, ref StaticData); } else { Time += ((240 / StaticData.CurrentBPM) * StaticData.BeatMultiplier); } } succeed = true; }