@@ -61,9 +61,26 @@ public class UgcParser : IParser<UgcChart>
6161 }
6262 }
6363
64+ FinalizeUgcSflDurations ( chart ) ;
6465 return ( chart , alerts ) ;
6566 }
6667
68+ private static void FinalizeUgcSflDurations ( UgcChart chart )
69+ {
70+ if ( chart . SflList . Count == 0 ) return ;
71+ chart . SflList = chart . SflList . OrderBy ( s => s . Time ) . ToList ( ) ;
72+ var endTime = Utils . Max ( chart . SflList [ ^ 1 ] . Time , chart . Notes . Max ( x=> x . EndTime ) ) ;
73+
74+ for ( var i = 0 ; i < chart . SflList . Count ; i ++ )
75+ {
76+ var t = chart . SflList [ i ] . Time ;
77+ var dur = ( i < chart . SflList . Count - 1 ? chart . SflList [ i + 1 ] . Time : endTime ) - t ;
78+ chart . SflList [ i ] = chart . SflList [ i ] with { Duration = dur . CanonicalForm } ;
79+ }
80+
81+ chart . SflList = chart . SflList . Where ( x => x . Multiplier != 1 ) . ToList ( ) ; // 倍率为1的,没必要放进来的
82+ }
83+
6784 private static void ParseHeaderLine ( string line , UgcChart chart , List < Alert > alerts , int lineNum )
6885 {
6986 if ( ! line . StartsWith ( '@' ) )
@@ -154,10 +171,7 @@ private static void ParseHeaderLine(string line, UgcChart chart, List<Alert> ale
154171 {
155172 var measureOffset = bpmPart [ ..bpmSpaceIdx ] ;
156173 var bpmValueStr = bpmPart [ ( bpmSpaceIdx + 1 ) ..] ;
157- var apostropheIdx = measureOffset . IndexOf ( '\' ' ) ;
158- if ( apostropheIdx > 0
159- && int . TryParse ( measureOffset [ ..apostropheIdx ] , NumberStyles . Integer , CultureInfo . InvariantCulture , out var bpmMeasure )
160- && int . TryParse ( measureOffset [ ( apostropheIdx + 1 ) ..] , NumberStyles . Integer , CultureInfo . InvariantCulture , out var bpmOffset )
174+ if ( TryParseUgcMeasureTick ( measureOffset , out var bpmMeasure , out var bpmOffset )
161175 && decimal . TryParse ( bpmValueStr , NumberStyles . Float , CultureInfo . InvariantCulture , out var bpmValue ) )
162176 {
163177 var tpm = chart . TicksPerBeat * 4 ;
@@ -182,20 +196,40 @@ private static void ParseHeaderLine(string line, UgcChart chart, List<Alert> ale
182196 break ;
183197
184198 case "@SPDMOD" :
199+ {
200+ var parts = value . Split ( '\t ' , StringSplitOptions . RemoveEmptyEntries | StringSplitOptions . TrimEntries ) ;
201+ if ( parts . Length >= 2
202+ && TryParseUgcMeasureTick ( parts [ 0 ] , out var meas , out var tick )
203+ && decimal . TryParse ( parts [ 1 ] , NumberStyles . Float , CultureInfo . InvariantCulture , out var mult ) )
185204 {
186- var parts = value . Split ( [ '\t ' , ' ' ] , StringSplitOptions . RemoveEmptyEntries ) ;
187- if ( parts . Length >= 2 && int . TryParse ( parts [ 0 ] , NumberStyles . Integer , CultureInfo . InvariantCulture , out var tilMeasure )
188- && double . TryParse ( parts [ 1 ] , NumberStyles . Float , CultureInfo . InvariantCulture , out var tilMult ) )
189- chart . SflList . Add ( ( tilMeasure , Rational . Zero , ( decimal ) tilMult ) ) ;
205+ var tpm = chart . TicksPerBeat * 4 ;
206+ chart . SflList . Add ( ( meas + new Rational ( tick , tpm ) , Rational . Zero , mult ) ) ;
190207 }
208+ else
209+ alerts . Add ( new Alert ( Warning , $ "@SPDMOD 格式错误: { line } ") { Line = lineNum } ) ;
191210 break ;
211+ }
192212
193213 default :
194214 alerts . Add ( new Alert ( Info , $ "未知头部标签: { tag } ") { Line = lineNum } ) ;
195215 break ;
196216 }
197217 }
198218
219+ /** UGC 时刻字符串 measure'tick(@BPM、@SPDMOD、音符行 #m't 共用)。 */
220+ private static bool TryParseUgcMeasureTick ( string measureTick , out int measure , out int tick )
221+ {
222+ measure = 0 ;
223+ tick = 0 ;
224+ measureTick = measureTick . Trim ( ) ;
225+ var ap = measureTick . IndexOf ( '\' ' ) ;
226+ if ( ap <= 0 )
227+ return false ;
228+
229+ return int . TryParse ( measureTick [ ..ap ] , NumberStyles . Integer , CultureInfo . InvariantCulture , out measure )
230+ && int . TryParse ( measureTick [ ( ap + 1 ) ..] , NumberStyles . Integer , CultureInfo . InvariantCulture , out tick ) ;
231+ }
232+
199233 private static int ParseNoteLine ( string [ ] lines , int idx , UgcChart chart , List < Alert > alerts )
200234 {
201235 var line = lines [ idx ] ;
@@ -219,21 +253,15 @@ private static int ParseNoteLine(string[] lines, int idx, UgcChart chart, List<A
219253 var prefix = line [ ..colonIdx ] ;
220254 var code = line [ ( colonIdx + 1 ) ..] ;
221255 var hashIdx = prefix . IndexOf ( '#' ) ;
222- var apostropheIdx = prefix . IndexOf ( '\' ' ) ;
223- if ( hashIdx < 0 || apostropheIdx < 0 || apostropheIdx <= hashIdx + 1 )
256+ if ( hashIdx < 0 )
224257 {
225258 alerts . Add ( new Alert ( Warning , $ "音符行前缀格式错误: { line } ") { Line = lineNum } ) ;
226259 return idx ;
227260 }
228261
229- if ( ! int . TryParse ( prefix [ ( hashIdx + 1 ) ..apostropheIdx ] , NumberStyles . Integer , CultureInfo . InvariantCulture , out var measure ) )
230- {
231- alerts . Add ( new Alert ( Warning , $ "无法解析 measure: { line } ") { Line = lineNum } ) ;
232- return idx ;
233- }
234- if ( ! int . TryParse ( prefix [ ( apostropheIdx + 1 ) ..] , NumberStyles . Integer , CultureInfo . InvariantCulture , out var tick ) )
262+ if ( ! TryParseUgcMeasureTick ( prefix [ ( hashIdx + 1 ) ..] , out var measure , out var tick ) )
235263 {
236- alerts . Add ( new Alert ( Warning , $ "无法解析 tick: { line } ") { Line = lineNum } ) ;
264+ alerts . Add ( new Alert ( Warning , $ "无法解析 measure' tick: { line } ") { Line = lineNum } ) ;
237265 return idx ;
238266 }
239267
0 commit comments