@@ -267,7 +267,7 @@ private static int ParseNoteLine(string[] lines, int idx, ChuChart chart, List<A
267267 return idx ;
268268 }
269269
270- var note = new ChuNote
270+ ChuNote ? note = new ChuNote
271271 {
272272 Time = measure + new Rational ( tick , RSL ) ,
273273 } ;
@@ -277,7 +277,10 @@ private static int ParseNoteLine(string[] lines, int idx, ChuChart chart, List<A
277277 switch ( typeChar )
278278 {
279279 case 't' :
280- ParseTapNote ( code , note , alerts , lineNum , chart ) ;
280+ ParseTapNote ( code , note , alerts , lineNum , chart , false ) ;
281+ break ;
282+ case 'x' :
283+ ParseTapNote ( code , note , alerts , lineNum , chart , true ) ;
281284 break ;
282285
283286 case 'h' :
@@ -286,25 +289,23 @@ private static int ParseNoteLine(string[] lines, int idx, ChuChart chart, List<A
286289
287290 case 's' :
288291 idx = ParseSlideNote ( lines , idx , code , note , alerts , chart ) ;
292+ note = null ; // ParseSlideNote中,会自己构造note并自己添加进chart。因此这里默认的统一note不应被添加进chart。
289293 break ;
290294
291295 case 'a' :
292296 ParseAirNote ( code , note , alerts , lineNum , chart ) ;
293297 break ;
294298
295- case 'x' :
296- ParseChrNote ( code , note , alerts , lineNum , chart ) ;
297- break ;
298-
299299 case 'f' :
300300 note . Type = "FLK" ;
301301 ParseCellWidth ( code , 1 , note , alerts , lineNum , chart ) ;
302302 if ( code . Length > 3 )
303303 note . Tag = code [ 3 ..] ;
304304 break ;
305305
306- case 'c' :
307- return idx ; // Margrete Air Crush, silently skip
306+ case 'c' : // Umiguri的CLICK音符,疑似在C2s中是没有对应的。这个音符没有Cell和Width,除了Type什么都没有,所以直接存下来就可以了。
307+ note . Type = "CLICK" ;
308+ break ;
308309
309310 case 'd' :
310311 note . Type = "MNE" ;
@@ -316,14 +317,20 @@ private static int ParseNoteLine(string[] lines, int idx, ChuChart chart, List<A
316317 return idx ;
317318 }
318319
319- chart . Notes . Add ( note ) ;
320+ if ( note != null ) chart . Notes . Add ( note ) ;
320321 return idx ;
321322 }
322323
323- private static void ParseTapNote ( string code , ChuNote note , List < Alert > alerts , int lineNum , ChuChart chart )
324+ private static void ParseTapNote ( string code , ChuNote note , List < Alert > alerts , int lineNum , ChuChart chart , bool isCHR )
324325 {
325326 note . Type = "TAP" ;
326327 ParseCellWidth ( code , 1 , note , alerts , lineNum , chart ) ;
328+ if ( isCHR )
329+ {
330+ note . Type = "CHR" ;
331+ var extraRaw = code . Length > 3 ? code [ 3 ..] : "" ;
332+ note . Tag = ChrExtras . GetValueOrDefault ( extraRaw , extraRaw ) ;
333+ }
327334 }
328335
329336 private static int ParseHoldNote ( string [ ] lines , int idx , string code , ChuNote note , List < Alert > alerts , ChuChart chart )
@@ -335,7 +342,7 @@ private static int ParseHoldNote(string[] lines, int idx, string code, ChuNote n
335342 while ( idx + 1 < lines . Length )
336343 {
337344 var nextLine = lines [ idx + 1 ] . Trim ( ) ;
338- if ( ! TryParseFollowerLine ( nextLine , out var duration , out _ , out _ ) )
345+ if ( ! TryParseFollowerLine ( nextLine , out _ , out var duration , out _ , out _ ) )
339346 {
340347 if ( nextLine . StartsWith ( '\' ' ) || nextLine . StartsWith ( '@' ) ) { idx ++ ; continue ; }
341348 break ;
@@ -351,48 +358,59 @@ private static int ParseHoldNote(string[] lines, int idx, string code, ChuNote n
351358 return idx ;
352359 }
353360
354- private static int ParseSlideNote ( string [ ] lines , int idx , string code , ChuNote note , List < Alert > alerts , ChuChart chart )
361+ private static int ParseSlideNote ( string [ ] lines , int idx , string code , ChuNote previousNote , List < Alert > alerts , ChuChart chart )
355362 {
356- note . Type = "SLD" ;
357- ParseCellWidth ( code , 1 , note , alerts , idx + 1 , chart ) ;
363+ // 注:一开始从外面传进来的previousNote,最后并不会被添加进chart里,只是作为第一段的起点参照而已。
364+ var startTime = previousNote . Time ;
365+ ParseCellWidth ( code , 1 , previousNote , alerts , idx + 1 , chart ) ;
366+ previousNote . EndCell = previousNote . Cell ;
367+ previousNote . EndWidth = previousNote . Width ;
358368
359369 bool foundFirst = false ;
360370 while ( idx + 1 < lines . Length )
361- {
371+ { // 循环处理所有的跟随行。idx始终指向上一条已经处理完的行。
362372 var nextLine = lines [ idx + 1 ] . Trim ( ) ;
363- if ( ! TryParseFollowerLine ( nextLine , out var duration , out var endCell , out var endWidth ) )
373+ if ( ! TryParseFollowerLine ( nextLine , out var marker , out var duration , out var endCell , out var endWidth , true ) )
364374 {
365375 if ( nextLine . StartsWith ( '\' ' ) || nextLine . StartsWith ( '@' ) ) { idx ++ ; continue ; }
366376 break ;
367377 }
368378
369- note . Duration += new Rational ( duration , RSL ) ;
370- note . EndCell = endCell ;
371- note . EndWidth = endWidth ;
379+ var segmentEnd = startTime + new Rational ( duration , RSL ) ;
380+ var note = new ChuNote
381+ {
382+ Type = marker == "s" ? "SLD" : "SLC" ,
383+ Time = previousNote . EndTime , Cell = previousNote . EndCell , Width = previousNote . EndWidth ,
384+ Duration = segmentEnd - previousNote . EndTime ,
385+ EndCell = endCell , EndWidth = endWidth ,
386+ TargetNote = "SLD"
387+ } ;
388+ chart . Notes . Add ( note ) ;
389+ previousNote = note ;
372390 idx ++ ;
373391 foundFirst = true ;
374392 }
375393
376394 if ( ! foundFirst )
377- alerts . Add ( new Alert ( Warning , $ "SLD 音符缺少时长跟随行") { Line = idx + 1 , RelevantNote = FormatNoteRef ( note , chart ) } ) ;
395+ alerts . Add ( new Alert ( Warning , $ "SLD 音符缺少时长跟随行") { Line = idx + 1 , RelevantNote = FormatNoteRef ( previousNote , chart ) } ) ;
378396
379397 return idx ;
380398 }
381399
382- private static bool TryParseFollowerLine ( string line , out int duration , out int endCell , out int endWidth , bool requireEndCellWidth = false )
400+ private static bool TryParseFollowerLine ( string line , out string marker , out int duration , out int endCell , out int endWidth , bool requireEndCellWidth = false )
383401 {
384402 duration = 0 ;
385403 endCell = 0 ;
386404 endWidth = 1 ;
405+ marker = "" ;
387406
388407 if ( ! line . StartsWith ( '#' ) ) return false ;
389408
390409 // support both >s (SLD) and >c (SLC) follower lines
391- int gtIdx = - 1 ;
392- int markerLen = 0 ;
393- if ( line . Contains ( ">s" ) ) { gtIdx = line . IndexOf ( ">s" , StringComparison . Ordinal ) ; markerLen = 2 ; }
394- else if ( line . Contains ( ">c" ) ) { gtIdx = line . IndexOf ( ">c" , StringComparison . Ordinal ) ; markerLen = 2 ; }
410+ int gtIdx = line . IndexOfAny ( [ '>' , ':' ] ) ;
395411 if ( gtIdx < 1 ) return false ;
412+ marker = line [ gtIdx + 1 ] . ToString ( ) ;
413+ int markerLen = 2 ;
396414
397415 var durationStr = line [ 1 ..gtIdx ] ;
398416 if ( ! int . TryParse ( durationStr , NumberStyles . Integer , CultureInfo . InvariantCulture , out duration ) ) return false ;
@@ -467,20 +485,6 @@ private static void ParseAirNote(string code, ChuNote note, List<Alert> alerts,
467485 }
468486 }
469487
470- private static void ParseChrNote ( string code , ChuNote note , List < Alert > alerts , int lineNum , ChuChart chart )
471- {
472- note . Type = "CHR" ;
473- if ( code . Length < 3 )
474- {
475- alerts . Add ( new Alert ( Warning , $ "CHR 音符代码过短: { code } ") { Line = lineNum } ) ;
476- return ;
477- }
478-
479- ParseCellWidth ( code , 1 , note , alerts , lineNum , chart ) ;
480- var extraRaw = code . Length > 3 ? code [ 3 ..] : "" ;
481- note . Tag = ChrExtras . GetValueOrDefault ( extraRaw , extraRaw ) ;
482- }
483-
484488 private static int HexCharToInt ( char c )
485489 {
486490 return c switch
0 commit comments