@@ -83,77 +83,75 @@ class Chart {
8383 return data ;
8484 }
8585
86- public static function loadChartMeta (songName : String , difficulty : String = ' ' , fromMods : Bool = true , includeMetaDifficulties : Bool = true ): ChartMetaData {
87- var metaPath = Paths .file (' songs/ ${songName }/meta.json' );
88- var metaDiffPath = Paths .file (' songs/ ${songName }/meta- ${difficulty }.json' );
89-
90- var data : ChartMetaData = null , fromDifficulty = false ;
91- var fromMods : Bool = fromMods ;
92- for (path in [metaDiffPath , metaPath ]) if (Assets .exists (path )) {
86+ public static function loadChartMeta (songName : String , ? variant : String , fromMods : Bool = true , includeMetaVariations = true ): ChartMetaData {
87+ var defaultPath = Paths .file (' songs/ $songName /meta.json' ), isVariant = false ;
88+ var data : ChartMetaData = null , paths = (variant == null || variant == ' ' ) ? [defaultPath ] : [Paths .file (' songs/ $songName /meta- $variant .json' ), defaultPath ];
89+ for (path in paths ) if (Assets .exists (path )) {
9390 fromMods = Paths .assetsTree .existsSpecific (path , " TEXT" , MODS );
9491 try {
9592 var tempData = Json .parse (Assets .getText (path ));
9693 tempData .color = CoolUtil .getColorFromDynamic (tempData .color ).getDefault (Flags .DEFAULT_COLOR );
9794 data = tempData ;
98- } catch (e ) Logs .trace (' Failed to load song metadata for ${songName } ( $path ): ${Std .string (e )}' , ERROR );
95+ } catch (e ) Logs .trace (' Failed to load song metadata for $songName ( $path ): ${Std .string (e )}' , ERROR );
96+
9997 if (data != null ) {
100- fromDifficulty = path == metaDiffPath ;
98+ isVariant = path != defaultPath ;
10199 break ;
102100 }
103101 }
104102
105- if (data == null ) {
106- data = {
107- name : songName ,
108- bpm : Flags .DEFAULT_BPM
109- };
110- }
111- else {
112- data .name = songName ;
113- data .setFieldDefault (" bpm" , Flags .DEFAULT_BPM );
114- }
103+ if (data != null ) data .name = songName ;
104+ else data = {
105+ name : songName ,
106+ color : Flags .DEFAULT_COLOR
107+ };
108+
109+ if (isVariant ) data .variant = variant ;
110+ else data .variant = null ;
115111
116112 data .setFieldDefault (" displayName" , data .name );
113+
114+ data .setFieldDefault (" bpm" , Flags .DEFAULT_BPM );
117115 data .setFieldDefault (" beatsPerMeasure" , Flags .DEFAULT_BEATS_PER_MEASURE );
118116 data .setFieldDefault (" stepsPerBeat" , Flags .DEFAULT_STEPS_PER_BEAT );
119117 data .setFieldDefault (" icon" , Flags .DEFAULT_HEALTH_ICON );
120- data .setFieldDefault (" difficulties" , []);
121118 data .setFieldDefault (" coopAllowed" , Flags .DEFAULT_COOP_ALLOWED );
122119 data .setFieldDefault (" opponentModeAllowed" , Flags .DEFAULT_OPPONENT_MODE_ALLOWED );
123120 data .setFieldDefault (" instSuffix" , " " );
124121 data .setFieldDefault (" vocalsSuffix" , " " );
125122 data .setFieldDefault (" needsVoices" , true );
123+ data .setFieldDefault (" difficulties" , []);
124+ data .setFieldDefault (" variants" , []);
126125
127126 if (data .difficulties .length <= 0 ) {
128- data .difficulties = [for (f in Paths .getFolderContent (' songs/ ${songName }/charts/' , false , fromMods ? MODS : SOURCE )) if (Path .extension (f .toUpperCase ()) == " JSON" ) Path .withoutExtension (f )];
129- var tempDiffs = [];
127+ var path = ' songs/ $songName /charts/' ;
128+ if (isVariant ) path + = ' $variant /' ;
129+
130+ data .difficulties = [for (f in Paths .getFolderContent (path , false , fromMods ? MODS : SOURCE )) if (Path .extension (f .toUpperCase ()) == " JSON" ) Path .withoutExtension (f )];
130131 if (data .difficulties .length == 3 ) {
131- for (d in data .difficulties ) {
132- switch (d .toLowerCase ()) {
133- case " easy" : tempDiffs .insert (0 , d );
134- case " normal" : tempDiffs .insert (1 , d );
135- case " hard" : tempDiffs .insert (2 , d );
136- }
132+ var tempDiffs = [];
133+ for (d in data .difficulties ) switch (d .toLowerCase ()) {
134+ case " easy" : tempDiffs .insert (0 , d );
135+ case " normal" : tempDiffs .insert (1 , d );
136+ case " hard" : tempDiffs .insert (2 , d );
137137 }
138- data .difficulties = tempDiffs ;
138+ if ( tempDiffs . length == 3 ) data .difficulties = tempDiffs ;
139139 }
140140 }
141141
142- if (includeMetaDifficulties && ! fromDifficulty ) {
143- data .metas = [];
144- for (difficulty in data .difficulties ) {
145- if (! data .metas .exists (difficulty ) && Assets .exists (Paths .file (' songs/ ${songName }/meta- ${difficulty }.json' )))
146- data .metas .set (difficulty , loadChartMeta (songName , difficulty , fromMods , false ));
147- }
142+ data .metas = [];
143+ if (includeMetaVariations && data .variants .length > 0 ) for (variant in data .variants ) {
144+ if (! data .metas .exists (variant ) && Assets .exists (Paths .file (' songs/ $songName /meta- $variant .json' )))
145+ data .metas .set (variant , loadChartMeta (songName , variant , fromMods ));
148146 }
149147
150148 return data ;
151149 }
152150
153- public static function parse (songName : String , ? difficulty : String ): ChartData {
151+ public static function parse (songName : String , ? difficulty : String , ? variant : String ): ChartData {
154152 if (difficulty == null ) difficulty = Flags .DEFAULT_DIFFICULTY ;
155153
156- var chartPath = Paths .chart (songName , difficulty );
154+ var chartPath = Paths .chart (songName , difficulty , variant );
157155 var base : ChartData = {
158156 strumLines : [],
159157 noteTypes : [],
@@ -167,15 +165,15 @@ class Chart {
167165 fromMods : Paths .assetsTree .existsSpecific (chartPath , " TEXT" , MODS )
168166 };
169167
170- var valid : Bool = true ;
168+ var valid : Bool = true , namePrint = ' $ songName $ difficulty ' + (( variant != null && variant != ' ' ) ? ' ( $ variant ) ' : ' ' ) ;
171169 if (! Assets .exists (chartPath )) {
172- Logs .error (' Chart for song ${ songName } ( $ difficulty ) at " $chartPath " was not found.' );
170+ Logs .error (' Chart for song $namePrint at " $chartPath " was not found.' );
173171 valid = false ;
174172 }
175173 var data : Dynamic = null ;
176174 if (valid ) {
177175 try data = Json .parse (Assets .getText (chartPath ))
178- catch (e ) Logs .trace (' Could not parse chart for song ${ songName } ( $ difficulty ) : ${Std .string (e )}' , ERROR , RED );
176+ catch (e ) Logs .trace (' Could not parse chart for song $namePrint : ${Std .string (e )}' , ERROR , RED );
179177 }
180178
181179 /**
@@ -207,13 +205,12 @@ class Chart {
207205 }
208206 #end
209207
210- var loadedMeta = loadChartMeta (songName , difficulty , base .fromMods );
208+ var loadedMeta = loadChartMeta (songName , variant , base .fromMods , false );
211209 if (base .meta == null ) base .meta = loadedMeta ;
212210 else {
213211 for (field in Reflect .fields (base .meta )) {
214212 var f = Reflect .field (base .meta , field );
215- if (f != null )
216- Reflect .setField (loadedMeta , field , f );
213+ if (f != null ) Reflect .setField (loadedMeta , field , f );
217214 }
218215 base .meta = loadedMeta ;
219216 }
@@ -223,8 +220,7 @@ class Chart {
223220 */
224221 #if REGION
225222 var extraEvents : Array <ChartEvent > = loadEventsJson (songName );
226- if (extraEvents != null )
227- base .events = base .events .concat (extraEvents );
223+ if (extraEvents != null ) base .events = base .events .concat (extraEvents );
228224 #end
229225
230226 /**
@@ -252,61 +248,60 @@ class Chart {
252248
253249 /**
254250 * Saves the chart to the specific song folder path.
255- * @param songFolderPath Path to the song folder (ex: `mods/your mod/songs/song/`)
256- * @param chart Chart to save
257- * @param difficulty Name of the difficulty
258- * @param saveSettings
251+ * @param chart Chart to save.
252+ * @param difficulty Name of the difficulty (Optional).
253+ * @param variant Name of the Variant (Optional).
254+ * @param saveSettings (Optional).
259255 * @return Filtered chart used for saving.
260256 */
261- public static function save (songFolderPath : String , chart : ChartData , ? difficulty : String , ? saveSettings : ChartSaveSettings ): ChartData {
257+ public static function save (chart : ChartData , ? difficulty : String , ? variant : String , ? saveSettings : ChartSaveSettings ): ChartData {
262258 if (difficulty == null ) difficulty = Flags .DEFAULT_DIFFICULTY ;
263259 if (saveSettings == null ) saveSettings = {};
264260
265- if (saveSettings .saveMetaInChart == null ) saveSettings .saveMetaInChart = true ;
266- if (saveSettings .saveLocalEvents == null ) saveSettings .saveLocalEvents = true ;
267- if (saveSettings .saveGlobalEvents == null ) saveSettings .saveGlobalEvents = false ;
268-
269- var filteredChart = filterChartForSaving (chart , saveSettings .saveMetaInChart , saveSettings .saveLocalEvents , saveSettings .saveGlobalEvents );
270- var meta = filteredChart .meta ;
261+ var filteredChart = filterChartForSaving (chart , saveSettings .saveMetaInChart , saveSettings .saveLocalEvents , saveSettings .saveGlobalEvents && saveSettings .seperateGlobalEvents != true );
271262
272263 #if sys
273- var saveFolder : String = saveSettings .folder == null ? " charts" : saveSettings .folder ;
264+ var songPath = saveSettings .songFolder == null ? ' assets/songs/ ${chart .meta .name }' : saveSettings .songFolder ;
265+ var metaPath = ' meta.json' , prettyPrint = saveSettings .prettyPrint == true ? Flags .JSON_PRETTY_PRINT : null , temp : String ;
266+ if ((temp = Paths .assetsTree .getPath (' $songPath / $metaPath ' )) != null ) {
267+ songPath = temp .substr (0 , temp .length - metaPath .length - 1 );
268+ metaPath = temp ;
269+ }
270+ else if (saveSettings .songFolder == null )
271+ metaPath = (songPath = ' ${Paths .getAssetsRoot ()}/ $songPath ' ) + ' / $metaPath ' ;
272+
273+ var chartFolder = saveSettings .folder == null ? ((variant == null || variant == ' ' ) ? ' charts' : ' charts/ $variant ' ) : saveSettings .folder ;
274+ var chartPath = ' $songPath / $chartFolder / ${difficulty .trim ()}.json' ;
274275
275- if (! FileSystem . exists ( ' ${ songFolderPath } / $ saveFolder / ' ) )
276- FileSystem . createDirectory ( ' ${ songFolderPath } / $ saveFolder / ' );
276+ if (saveSettings . saveChart == null || saveSettings . saveChart == true )
277+ CoolUtil . safeSaveFile ( chartPath , Json . stringify ( filteredChart , null , prettyPrint ) );
277278
278- var chartPath = ' ${ songFolderPath } / $ saveFolder / ${ difficulty . trim ()} .json ' ;
279- var metaPath = ' ${ songFolderPath } /meta.json ' ;
279+ if ( saveSettings . overrideExistingMeta || ! FileSystem . exists ( metaPath ))
280+ CoolUtil . safeSaveFile ( metaPath , Json . stringify ( filterMetaForSaving ( chart . meta ), null , prettyPrint )) ;
280281
281- CoolUtil .safeSaveFile (chartPath , Json .stringify (filteredChart , null , saveSettings .prettyPrint == true ? Flags .JSON_PRETTY_PRINT : null ));
282+ if (saveSettings .seperateGlobalEvents == true ) {
283+ var eventsPath = ' $songPath /events.json' , events = filterEventsForSaving (chart .events , false , true );
282284
283- if (meta != null && (saveSettings .overrideExistingMeta || ! FileSystem .exists (metaPath )))
284- CoolUtil .safeSaveFile (metaPath , makeMetaSaveable (meta ));
285+ if (events .length != 0 ) CoolUtil .safeSaveFile (eventsPath , Json .stringify ({events : events }, null , prettyPrint ));
286+ else if (FileSystem .exists (eventsPath )) FileSystem .deleteFile (eventsPath );
287+ }
285288 #end
289+
286290 return filteredChart ;
287291 }
288292
289- public static function filterChartForSaving (chart : ChartData , ? saveMetaInChart : Bool , ? saveLocalEvents : Bool , ? saveGlobalEvents : Bool ): ChartData {
293+ public static function filterChartForSaving (chart : ChartData , saveMetaInChart = true , ? saveLocalEvents : Bool , ? saveGlobalEvents : Bool ): ChartData {
294+ var meta = chart .meta , events = chart .events ;
295+ chart .meta = null ;
296+ chart .events = null ;
297+
290298 var data = Reflect .copy (chart ); // make a copy of the chart to leave the OG intact
291- if (saveMetaInChart != true ) {
292- data .meta = null ;
293- } else {
294- data .meta = Reflect .copy (chart .meta ); // also make a copy of the metadata to leave the OG intact.
295- if (data .meta != null && Reflect .hasField (data .meta , " parsedColor" )) Reflect .deleteField (data .meta , " parsedColor" );
296- }
297299
298- // in this part abt the events, i gotta account that these booleans can be null - Nex
299- if (saveLocalEvents != true && saveGlobalEvents != true ) data .events = null ;
300- else {
301- data .events = [];
302- for (event in chart .events ) if ((saveLocalEvents == true && event .global != true ) || (saveGlobalEvents == true && event .global == true )) {
303- var copy = Reflect .copy (event );
304- if (saveLocalEvents == true ? event .global != true : event .global == true ) Reflect .deleteField (copy , " global" ); // should NOT delete the field when saving with the local events and the event should have been global - Nex
305- data .events .push (copy );
306- }
307- if (data .events .length == 0 ) data .events = null ;
308- }
300+ chart .meta = meta ;
301+ chart .events = events ;
309302
303+ data .meta = saveMetaInChart ? filterMetaForSaving (meta ) : null ;
304+ data .events = filterEventsForSaving (events , saveLocalEvents , saveGlobalEvents );
310305 data .fromMods = null ;
311306
312307 var sortedData : Dynamic = {};
@@ -318,19 +313,46 @@ class Chart {
318313 return sortedData ;
319314 }
320315
321- public static inline function makeMetaSaveable (meta : ChartMetaData , prettyPrint : Bool = true ): String {
316+ public static function filterEventsForSaving (events : Array <ChartEvent >, saveLocalEvents = true , saveGlobalEvents = false ): Array <ChartEvent > {
317+ var data = [];
318+ if (! saveLocalEvents && ! saveGlobalEvents ) return data ;
319+
320+ for (event in events ) if ((saveLocalEvents && event .global != true ) || (saveGlobalEvents && event .global == true )) {
321+ var copy = Reflect .copy (event );
322+ if (saveLocalEvents ? event .global != true : event .global == true ) Reflect .deleteField (copy , " global" ); // should NOT delete the field when saving with the local events and the event should have been global - Nex
323+ data .push (copy );
324+ }
325+
326+ return data ;
327+ }
328+
329+ public static inline function makeMetaSaveable (meta : ChartMetaData , prettyPrint : Bool = true ): String
330+ return Json .stringify (filterMetaForSaving (meta ), null , prettyPrint ? Flags .JSON_PRETTY_PRINT : null );
331+
332+ public static inline function filterMetaForSaving (meta : ChartMetaData ): ChartMetaData {
322333 var data : Dynamic = Reflect .copy (meta );
323- if (data .color != null ) data .color = FlxColor .fromInt (data .color ).toWebString (); // dont even ask me - Nex
334+ if (data .color != null ) data .color = FlxColor .fromInt (data .color ).toWebString (); // dont even ask me - Nex
335+ Reflect .deleteField (data , ' parsedColor' );
324336 Reflect .deleteField (data , ' metas' );
325- return Json .stringify (data , null , prettyPrint ? Flags .JSON_PRETTY_PRINT : null );
337+ Reflect .deleteField (data , " variant" );
338+ if (data .instSuffix != null && data .instSuffix == " " ) Reflect .deleteField (data , " instSuffix" );
339+ if (data .vocalsSuffix != null && data .vocalsSuffix == " " ) Reflect .deleteField (data , " vocalsSuffix" );
340+ if (data .variants != null && data .variants .length == 0 ) Reflect .deleteField (data , " variants" );
341+ return data ;
326342 }
327343}
328344
329345typedef ChartSaveSettings = {
330- var ? overrideExistingMeta : Bool ;
346+ var ? prettyPrint : Bool ;
347+
331348 var ? saveMetaInChart : Bool ;
332349 var ? saveLocalEvents : Bool ;
333350 var ? saveGlobalEvents : Bool ;
334- var ? prettyPrint : Bool ;
351+
352+ var ? saveChart : Bool ;
353+ var ? overrideExistingMeta : Bool ;
354+ var ? seperateGlobalEvents : Bool ;
355+
335356 var ? folder : String ;
357+ var ? songFolder : String ;
336358}
0 commit comments