@@ -22,9 +22,9 @@ enum abstract XMLAnimType(Int)
|
||||
var BEAT = 1;
|
||||
var LOOP = 2;
|
||||
|
||||
public static function fromString(str:String, def:XMLAnimType = NONE)
|
||||
public static function fromString(str:String, def:XMLAnimType = XMLAnimType.NONE)
|
||||
{
|
||||
return switch (str.trim().toLowerCase())
|
||||
return switch (StringTools.trim(str).toLowerCase())
|
||||
{
|
||||
case "none": NONE;
|
||||
case "beat" | "onbeat": BEAT;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package funkin.backend.chart;
|
||||
|
||||
import funkin.backend.system.Conductor;
|
||||
import funkin.backend.chart.ChartData;
|
||||
import flixel.util.FlxColor;
|
||||
import haxe.io.Path;
|
||||
@@ -13,12 +12,68 @@ import sys.FileSystem;
|
||||
|
||||
using StringTools;
|
||||
|
||||
enum abstract ChartFormat(Int) {
|
||||
var CODENAME = 0;
|
||||
var LEGACY = 1; // also used by many other engines (old Psych, Kade and more) - Nex
|
||||
var VSLICE = 2;
|
||||
var PSYCH_NEW = 3;
|
||||
|
||||
@:to public function toString():String {
|
||||
return switch(cast (this, ChartFormat)) {
|
||||
case CODENAME: "CODENAME";
|
||||
case LEGACY: "LEGACY";
|
||||
case VSLICE: "VSLICE";
|
||||
case PSYCH_NEW: "PSYCH_NEW";
|
||||
}
|
||||
}
|
||||
|
||||
public static function fromString(str:String, def:ChartFormat = ChartFormat.LEGACY) {
|
||||
str = str.toLowerCase();
|
||||
str = StringTools.replace(str, " ", "");
|
||||
str = StringTools.replace(str, "_", "");
|
||||
str = StringTools.replace(str, ".", "");
|
||||
|
||||
if(StringTools.startsWith(str, "psychv1") || StringTools.startsWith(str, "psych1"))
|
||||
return PSYCH_NEW;
|
||||
|
||||
return switch(str) {
|
||||
case "codename" | "codenameengine": CODENAME;
|
||||
case "newpsych" | "psychnew": PSYCH_NEW;
|
||||
default: def;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Chart {
|
||||
/**
|
||||
* Default background colors for songs without bg color
|
||||
*/
|
||||
public inline static var defaultColor:FlxColor = 0xFF9271FD;
|
||||
|
||||
public static function cleanSongData(data:Dynamic):Dynamic {
|
||||
if (Reflect.hasField(data, "song")) {
|
||||
var field:Dynamic = Reflect.field(data, "song");
|
||||
if (field != null && Type.typeof(field) == TObject) // Cant use Reflect.isObject, because it detects strings for some reason
|
||||
return field;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public static function detectChartFormat(data:Dynamic):ChartFormat {
|
||||
var __temp:Dynamic; // imma reuse this var so the program doesnt have to get values multiple times - Nex
|
||||
|
||||
if ((__temp = data.codenameChart) == true || __temp == "true")
|
||||
return CODENAME;
|
||||
|
||||
if (Reflect.hasField(data, "version") && Reflect.hasField(data, "scrollSpeed"))
|
||||
return VSLICE;
|
||||
|
||||
if ((__temp = cleanSongData(data).format) != null && __temp is String && StringTools.startsWith(__temp, "psych_v1"))
|
||||
return PSYCH_NEW;
|
||||
|
||||
return LEGACY;
|
||||
}
|
||||
|
||||
public static function loadEventsJson(songName:String) {
|
||||
var path = Paths.file('songs/${songName.toLowerCase()}/events.json');
|
||||
var data:Array<ChartEvent> = null;
|
||||
@@ -33,8 +88,9 @@ class Chart {
|
||||
}
|
||||
|
||||
public static function loadChartMeta(songName:String, difficulty:String = "normal", fromMods:Bool = true) {
|
||||
var metaPath = Paths.file('songs/${songName.toLowerCase()}/meta.json');
|
||||
var metaDiffPath = Paths.file('songs/${songName.toLowerCase()}/meta-${difficulty.toLowerCase()}.json');
|
||||
var songNameLower = songName.toLowerCase();
|
||||
var metaPath = Paths.file('songs/${songNameLower}/meta.json');
|
||||
var metaDiffPath = Paths.file('songs/${songNameLower}/meta-${difficulty.toLowerCase()}.json');
|
||||
|
||||
var data:ChartMetaData = null;
|
||||
var fromMods:Bool = fromMods;
|
||||
@@ -67,7 +123,7 @@ class Chart {
|
||||
data.setFieldDefault("parsedColor", data.color.getColorFromDynamic().getDefault(defaultColor));
|
||||
|
||||
if (data.difficulties.length <= 0) {
|
||||
data.difficulties = [for(f in Paths.getFolderContent('songs/${songName.toLowerCase()}/charts/', false, !fromMods)) if (Path.extension(f = f.toUpperCase()) == "JSON") Path.withoutExtension(f)];
|
||||
data.difficulties = [for(f in Paths.getFolderContent('songs/${songNameLower}/charts/', false, !fromMods)) if (Path.extension(f = f.toUpperCase()) == "JSON") Path.withoutExtension(f)];
|
||||
if (data.difficulties.length == 3) {
|
||||
var hasHard = false, hasNormal = false, hasEasy = false;
|
||||
for(d in data.difficulties) {
|
||||
@@ -116,12 +172,12 @@ class Chart {
|
||||
Logs.trace('Could not parse chart for song ${songName} ($difficulty): ${Std.string(e)}', ERROR, RED);
|
||||
}
|
||||
|
||||
if (data != null) {
|
||||
/**
|
||||
* CHART CONVERSION
|
||||
*/
|
||||
#if REGION
|
||||
if (Reflect.hasField(data, "codenameChart") && Reflect.field(data, "codenameChart") == true) {
|
||||
/**
|
||||
* CHART CONVERSION
|
||||
*/
|
||||
#if REGION
|
||||
if (data != null) switch (detectChartFormat(data)) {
|
||||
case CODENAME:
|
||||
// backward compat on events since its caused problems
|
||||
var eventTypesToString:Map<Int, String> = [
|
||||
-1 => "HScript Call",
|
||||
@@ -132,28 +188,23 @@ class Chart {
|
||||
];
|
||||
|
||||
if (data.events == null) data.events = [];
|
||||
for (event in cast(data.events, Array<Dynamic>)) {
|
||||
if (Reflect.hasField(event, "type")) {
|
||||
if(event.type != null)
|
||||
event.name = eventTypesToString[event.type];
|
||||
Reflect.deleteField(event, "type");
|
||||
}
|
||||
for (event in cast(data.events, Array<Dynamic>)) if (Reflect.hasField(event, "type")) {
|
||||
if (event.type != null)
|
||||
event.name = eventTypesToString[event.type];
|
||||
Reflect.deleteField(event, "type");
|
||||
}
|
||||
|
||||
// codename chart
|
||||
base = data;
|
||||
} else {
|
||||
// base game chart
|
||||
FNFLegacyParser.parse(data, base);
|
||||
}
|
||||
#end
|
||||
case PSYCH_NEW: PsychParser.parse(data, base);
|
||||
case VSLICE: // TODO
|
||||
case LEGACY: FNFLegacyParser.parse(data, base);
|
||||
}
|
||||
#end
|
||||
|
||||
if (base.meta == null)
|
||||
base.meta = loadChartMeta(songName, difficulty, base.fromMods);
|
||||
var loadedMeta = loadChartMeta(songName, difficulty, base.fromMods);
|
||||
if (base.meta == null) base.meta = loadedMeta;
|
||||
else {
|
||||
var loadedMeta = loadChartMeta(songName, difficulty, base.fromMods);
|
||||
for(field in Reflect.fields(base.meta)) {
|
||||
for (field in Reflect.fields(base.meta)) {
|
||||
var f = Reflect.field(base.meta, field);
|
||||
if (f != null)
|
||||
Reflect.setField(loadedMeta, field, f);
|
||||
|
||||
@@ -6,12 +6,7 @@ import funkin.backend.system.Conductor;
|
||||
class FNFLegacyParser {
|
||||
public static function parse(data:Dynamic, result:ChartData) {
|
||||
// base fnf chart parsing
|
||||
var data:SwagSong = data;
|
||||
if (Reflect.hasField(data, "song")) {
|
||||
var field:Dynamic = Reflect.field(data, "song");
|
||||
if (!(field is String))
|
||||
data = field;
|
||||
}
|
||||
var data:SwagSong = Chart.cleanSongData(data);
|
||||
|
||||
result.scrollSpeed = data.speed;
|
||||
result.stage = data.stage;
|
||||
@@ -29,7 +24,7 @@ class FNFLegacyParser {
|
||||
position: "boyfriend",
|
||||
notes: []
|
||||
});
|
||||
var gfName = data.gf != null ? data.gf : (data.gfVersion != null ? data.gfVersion : "gf");
|
||||
var gfName = data.gf != null ? data.gf : (data.gfVersion != null ? data.gfVersion : (data.player3 != null ? data.player3 : "gf"));
|
||||
if (!p2isGF && gfName != "none") {
|
||||
result.strumLines.push({
|
||||
characters: [gfName],
|
||||
@@ -214,6 +209,7 @@ typedef SwagSong =
|
||||
|
||||
var player1:String;
|
||||
var player2:String;
|
||||
var ?player3:String; // alt for gf but idr if it was just used in psych or what but eh whatever maybe its better here anyways - Nex
|
||||
var ?gf:String;
|
||||
var ?gfVersion:String;
|
||||
var validScore:Bool;
|
||||
|
||||
@@ -6,15 +6,33 @@ import funkin.backend.chart.FNFLegacyParser.SwagSong;
|
||||
import funkin.backend.system.Conductor;
|
||||
|
||||
class PsychParser {
|
||||
// Alreadly parsed in sections
|
||||
// Already parsed in sections
|
||||
public static var ignoreEvents:Array<String> = [
|
||||
"Camera Movement",
|
||||
"Alt Animation Toggle",
|
||||
"BPM Change"
|
||||
];
|
||||
|
||||
public static function parse(data:Dynamic, result:ChartData)
|
||||
/**
|
||||
* Converts 1.0 Psych charts to older ones
|
||||
*/
|
||||
public static function standardize(data:Dynamic, result:ChartData) {
|
||||
var sectionsData:Array<Dynamic> = Chart.cleanSongData(data).notes;
|
||||
if (sectionsData == null) return;
|
||||
|
||||
for (section in sectionsData) for (note in cast(section.sectionNotes, Array<Dynamic>)) {
|
||||
var gottaHitNote:Bool = section.mustHitSection == (note[1] >= 4);
|
||||
note[1] = (note[1] % 4) + (gottaHitNote ? 4 : 0);
|
||||
|
||||
if (note[3] != null && Std.isOfType(note[3], String))
|
||||
note[3] = Chart.addNoteType(result, note[3]);
|
||||
}
|
||||
}
|
||||
|
||||
public static function parse(data:Dynamic, result:ChartData) {
|
||||
if (Chart.detectChartFormat(data) == PSYCH_NEW) standardize(data, result);
|
||||
FNFLegacyParser.parse(data, result);
|
||||
}
|
||||
|
||||
public static function encode(chart:ChartData):Dynamic {
|
||||
var base:SwagSong = FNFLegacyParser.__convertToSwagSong(chart);
|
||||
|
||||
@@ -29,7 +29,6 @@ class Config {
|
||||
];
|
||||
|
||||
public static final DISALLOW_ABSTRACT_AND_ENUM = [
|
||||
"funkin.backend.FunkinSprite", // Error: String has no field trim, Due to Func
|
||||
"funkin.backend.scripting.events.PlayAnimEvent", // Error: expected member name or ';' after declaration specifiers, Due to Func
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user