diff --git a/README.md b/README.md index 53854c0c..6bfc3b08 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ haxelib install flixel haxelib install flixel-addons haxelib install flixel-ui haxelib install hxvlc +haxelib git moonchart https://github.com/MaybeMaru/moonchart haxelib git haxeui-openfl https://github.com/haxeui/haxeui-openfl haxelib git haxeui-core https://github.com/haxeui/haxeui-core haxelib git maru-hscript https://github.com/MaybeMaru/hscript-improved diff --git a/actions/libraries.json b/actions/libraries.json index 3459d440..dfe6e7ab 100644 --- a/actions/libraries.json +++ b/actions/libraries.json @@ -49,6 +49,13 @@ "type": "install", "version": null }, + { + "name": "moonchart", + "type": "git", + "dir": null, + "ref": "master", + "url": "https://github.com/MaybeMaru/moonchart" + }, { "name": "maru-hscript", "type": "git", diff --git a/project.xml b/project.xml index 997cc359..ad30a332 100644 --- a/project.xml +++ b/project.xml @@ -84,6 +84,7 @@ +
diff --git a/source/funkin/objects/NotesGroup.hx b/source/funkin/objects/NotesGroup.hx index f603dd69..af830704 100644 --- a/source/funkin/objects/NotesGroup.hx +++ b/source/funkin/objects/NotesGroup.hx @@ -106,7 +106,8 @@ class NotesGroup extends Group instance = this; game = isPlayState ? PlayState.instance : null; this.isPlayState = isPlayState; - SONG = Song.checkSong(song, null, false); //Double check null values + //SONG = Song.checkSong(song, null, false); //Double check null values + SONG = song; curSong = SONG.song; if (isPlayState) { diff --git a/source/funkin/states/PlayState.hx b/source/funkin/states/PlayState.hx index 882026c1..67424648 100644 --- a/source/funkin/states/PlayState.hx +++ b/source/funkin/states/PlayState.hx @@ -38,7 +38,7 @@ class PlayState extends MusicBeatState private var targetCamPos:FlxPoint; private static var prevCamFollow:FlxObject; - private var curSectionData:SectionJson; + private var curSectionData:Dynamic; public var notesGroup:NotesGroup; private var ratingGroup:RatingGroup; @@ -140,7 +140,7 @@ class PlayState extends MusicBeatState FlxG.cameras.setDefaultDrawTarget(camGame, true); persistentUpdate = persistentDraw = true; - SONG = Song.checkSong(SONG, null, false); + //SONG = Song.checkSong(SONG, null, false); #if discord_rpc detailsText = isStoryMode ? 'Story Mode: ${storyWeek.toUpperCase()}' : 'Freeplay'; @@ -363,7 +363,8 @@ class PlayState extends MusicBeatState Conductor.songPosition = -Conductor.crochet * 5; Conductor.setPitch(Conductor.songPitch); - curSectionData = Song.checkSection(SONG.notes[0]); + + curSectionData = SONG.notes[0]; cameraMovement(); if (skipCountdown) { diff --git a/source/funkin/states/editors/ChartingState.hx b/source/funkin/states/editors/ChartingState.hx index e9ee31d1..3cf8fa27 100644 --- a/source/funkin/states/editors/ChartingState.hx +++ b/source/funkin/states/editors/ChartingState.hx @@ -65,7 +65,7 @@ class ChartingState extends MusicBeatState camTop = new FlxCamera(); camTop.bgColor.alpha = 0; FlxG.cameras.add(camTop, false); - SONG = Song.checkSong(PlayState.SONG); + SONG = PlayState.SONG; notes = SONG.notes; Conductor.offset = Vector.fromArrayCopy(SONG.offsets); @@ -737,17 +737,19 @@ class ChartingState extends MusicBeatState public function loadJson(song:String):Void { stop(); - PlayState.SONG = Song.checkSong(Song.loadFromFile(PlayState.curDifficulty, song)); + //PlayState.SONG = Song.checkSong(Song.loadFromFile(PlayState.curDifficulty, song)); + PlayState.SONG = Song.loadFromFile(PlayState.curDifficulty, song); FlxG.resetState(); } public function loadAutosave():Void { - PlayState.SONG = Song.checkSong(Song.parseJson('', autoSaveChart)); + //PlayState.SONG = Song.checkSong(Song.parseJson('', autoSaveChart)); + PlayState.SONG = Json.parse(autoSaveChart);//Song.parseJson('', autoSaveChart); FlxG.resetState(); } public function autosaveSong():Void { - SONG = Song.checkSong(SONG); + //SONG = Song.checkSong(SONG); autoSaveChart = getSongString(); SaveData.setSave('autoSaveChart', autoSaveChart); SaveData.flushData(); @@ -767,8 +769,8 @@ class ChartingState extends MusicBeatState } } - public function saveChart() { - SONG = Song.checkSong(SONG); + public inline function saveChart() + { saveJson(getSongString("\t"), PlayState.curDifficulty); } @@ -776,9 +778,12 @@ class ChartingState extends MusicBeatState { var metaEvents:Array = []; notes.fastForEach((section, i) -> { - metaEvents.push(section.sectionEvents.length <= 0 ? {} : { - sectionEvents: section.sectionEvents.copy() - }); + if (section.sectionEvents.length > 0) + { + metaEvents.push(cast { + sectionEvents: section.sectionEvents.copy() + }); + } }); if (metaEvents.length > 1) { @@ -789,14 +794,12 @@ class ChartingState extends MusicBeatState else break; } } - - final meta:SongMeta = { + + saveJson({ diffs: [PlayState.curDifficulty], offsets: SONG.offsets.copy(), events: metaEvents - } - - saveJson(meta, "songMeta"); + }, "songMeta"); } override function getEvent(id:String, sender:Dynamic, data:Dynamic, ?params:Array):Void diff --git a/source/funkin/substates/NotesSubstate.hx b/source/funkin/substates/NotesSubstate.hx index 451254cf..2cd40b82 100644 --- a/source/funkin/substates/NotesSubstate.hx +++ b/source/funkin/substates/NotesSubstate.hx @@ -15,7 +15,8 @@ class NotesSubstate extends MusicBeatSubstate super(true, 0x98000000); this.position = position; - SONG = Song.checkSong(song); + //SONG = Song.checkSong(song); + SONG = song; notesGroup = new NotesGroup(SONG, false); notesGroup.skipStrumIntro = true; notesGroup.init(position - 50); diff --git a/source/funkin/util/backend/SongZip.hx b/source/funkin/util/backend/SongZip.hx index 66673f8e..14d0ec8c 100644 --- a/source/funkin/util/backend/SongZip.hx +++ b/source/funkin/util/backend/SongZip.hx @@ -2,8 +2,6 @@ package funkin.util.backend; import haxe.io.Path; import flixel.util.FlxArrayUtil; -import funkin.util.song.formats.QuaFormat; -import funkin.util.song.formats.OsuFormat; import funkin.states.editors.ModSetupState; enum ZipFormat { @@ -78,7 +76,7 @@ class SongZip { } static final UNZIP_FORMAT:Map = [ - "osu" => { + /*"osu" => { getMap: function(chart) return cast new OsuFormat(chart), convert: function(map) return OsuFormat.convertSong("", map), title: "Title", @@ -91,7 +89,7 @@ class SongZip { title: "Title", diff: "DifficultyName", audio: "AudioFile" - } + }*/ ]; static function unzipFormat(format:String, modPath:String, zipFiles:Array) { diff --git a/source/funkin/util/song/Song.hx b/source/funkin/util/song/Song.hx index 59107961..49efd971 100644 --- a/source/funkin/util/song/Song.hx +++ b/source/funkin/util/song/Song.hx @@ -1,189 +1,74 @@ package funkin.util.song; +import moonchart.backend.Optimizer; import haxe.Json; import flixel.util.FlxSort; -import funkin.util.song.formats.OsuFormat; -import funkin.util.song.formats.SmFormat; -import funkin.util.song.formats.QuaFormat; -import funkin.util.song.formats.GhFormat; -import funkin.util.song.formats.FunkinFormat; +import moonchart.backend.FormatDetector; +import moonchart.formats.fnf.FNFMaru; +import moonchart.formats.fnf.legacy.FNFLegacy; -abstract NoteJson(Array) from Array to Array -{ - public var time(get, set):Float; - inline function set_time(value):Float return this[0] = value; - inline function get_time():Float return this[0]; - - public var lane(get, set):Int; - inline function set_lane(value):Int return this[1] = value; - inline function get_lane():Int return this[1]; - - public var length(get, set):Float; - inline function set_length(value):Float return this[2] = value; - inline function get_length():Float return this[2] ?? 0; - - public var type(get, set):String; - inline function set_type(value):String return this[3] = value; - inline function get_type():String return this[3]; - - public inline function push(value:Dynamic) this.push(value); - public inline function pop() return this.pop(); -} - -abstract EventJson(Array) from Array to Array { - public var time(get, set):Float; - inline function set_time(value):Float return this[0] = value; - inline function get_time():Float return this[0]; - - public var name(get, set):String; - inline function set_name(value):String return this[1] = value; - inline function get_name():String return this[1]; - - public var values(get, set):Array; - inline function set_values(value):Array return this[2] = value; - inline function get_values():Array return this[2]; - - public inline function push(value:Dynamic) this.push(value); - public inline function pop() return this.pop(); -} - -typedef SectionJson = { - var ?sectionNotes:Array; - var ?sectionEvents:Array; - var ?mustHitSection:Bool; - var ?bpm:Float; - var ?changeBPM:Bool; -} - -typedef SongJson = { - var song:String; - var notes:Array; - var bpm:Float; - var speed:Float; - var offsets:Array; - var stage:String; - var players:Array; -} - -typedef SongMeta = { - var events:Array; - var offsets:Array; - var diffs:Array; - //var bpm:Float; -} +typedef SongJson = FNFMaruJsonFormat; +typedef SongMeta = FNFMaruMetaFormat; +typedef SectionJson = FNFMaruSection; +typedef NoteJson = FNFLegacyNote; +typedef EventJson = FNFMaruEvent; class Song { - private static final CHART_FORMATS:Array = [ - 'json', // Vanilla FNF - 'osu', // Osu! Mania - 'sm', 'ssc', // Stepmania - 'qua', // Quaver - 'chart' // Guitar Hero - ]; - public static function loadFromFile(diff:String, folder:String):SongJson { folder = formatSongFolder(folder); - - CHART_FORMATS.fastForEach((format, i) -> { - final chartPath:String = Paths.chart(folder, diff, format); - var meta = getSongMeta(folder); - if (meta != null) meta = meta.diffs.contains(diff) ? meta : null; // Only use if diff is included + + var formats:Array = []; + + @:privateAccess + for (format => data in FormatDetector.formatMap) + { + if (formats.indexOf(data.extension) == -1) + formats.push(data.extension); + } + + formats.fastForEach((format, i) -> + { + var chartPath:String = Paths.chart(folder, diff, format); if (Paths.exists(chartPath, TEXT)) { - switch (format) { - case 'json': return checkSong(parseJson(chartPath), meta, true, diff); // Funkin chart - case 'osu': return checkSong(OsuFormat.convertSong(chartPath), meta, false, diff); // Osu chart - case 'sm' | 'ssc': return checkSong(SmFormat.convert(chartPath, diff), meta, false, diff); // Stepmania chart - case 'qua': return checkSong(QuaFormat.convertSong(chartPath), meta, false, diff); // Quaver chart - case 'chart': return checkSong(GhFormat.convertSong(chartPath), meta, false, diff); // Guitar hero chart + + var format = FormatDetector.findFormat([chartPath]); + var maru:FNFMaru = new FNFMaru(); + + switch (format) + { + case FNF_MARU: + maru.fromFile(chartPath); + case _: + var chart = FormatDetector.createFormatInstance(format); + chart.fromFile(chartPath); + maru.fromFormat(chart); } - } - }); - trace('$folder-$diff CHART NOT FOUND'); + trace("LOADED CHART FROM FORMAT: " + format); + return maru.data.song; + } + }); - if (folder == "tutorial") if (diff == "hard") // Couldnt even find tutorial safe fail + // Couldnt even find tutorial safe fail + if (folder == "tutorial") if (diff == "hard") { - throw 'Failed to load chart'; + throw 'Failed loading chart.'; return null; } + trace('$folder-$diff CHART NOT FOUND'); return loadFromFile('hard', 'tutorial'); } - inline public static function getSongMeta(song:String):Null { + public static function getSongMeta(song:String):Null { var meta = CoolUtil.getFileContent(Paths.songMeta(song)); return meta.length > 0 ? cast Json.parse(meta) : null; } - //Check null values and remove unused format variables - public static function checkSong(?song:SongJson, ?meta:SongMeta, checkEngine:Bool = false, diff:String = ""):SongJson - { - if (checkEngine) { - meta = FunkinFormat.metaCheck(meta, diff); - song = FunkinFormat.songCheck(song, meta, diff); - } - - song = JsonUtil.checkJson(getDefaultSong(), song); - - if (song.notes.length != 0) { - song.notes.fastForEach((section, i) -> { - checkSection(section); - if (section.sectionNotes.length > 100 && !CoolUtil.debugMode) // Fuck off - return getDefaultSong(); - }); - } - else { - song.notes.push(getDefaultSection()); - } - - // Apply song metaData - if (meta != null) { - song.offsets = meta.offsets.copy(); - - meta.events.fastForEach((section, s) -> { - if (Reflect.hasField(section, "sectionEvents")) { - section.sectionEvents.copy().fastForEach((event, e) -> { - song.notes[s].sectionEvents.push(event); - }); - } - }); - } - - return song; - } - - public static function checkSection(section:Null = null):SectionJson - { - section = JsonUtil.checkJson(getDefaultSection(), section); - final foundNotes:Map = []; - final uniqueNotes:Array> = []; // Skip duplicate notes - - section.sectionNotes.fastForEach((n, i) -> { - var key = Math.floor(n[0]) + "-" + n[1] + "-" + n[3]; - if (!foundNotes.exists(key)) - { - if (n[1] > Conductor.STRUMS_LENGTH - 1) // Convert extra key charts to 4 key - { - if (n[3] == null) n.push("default-extra"); - else if (n[3] == 0) n.unsafeSet(3, "default-extra"); - n.unsafeSet(1, n[1] % Conductor.STRUMS_LENGTH); - } - - foundNotes.set(key, 0); - uniqueNotes.push(n); - } - }); - - foundNotes.clear(); - section.sectionNotes = uniqueNotes; - - return section; - } - public static function getSectionTime(song:SongJson, section:Int = 0):Float { var crochet:Float = (60000 / song.bpm); var time:Float = 0; @@ -233,72 +118,52 @@ class Song } //Removes unused variables for smaller size - public static function optimizeJson(input:SongJson, metaClear:Bool = false):SongJson + public static function optimizeJson(input:SongJson):SongJson { var song:SongJson = JsonUtil.copyJson(input); - song.notes.fastForEach((sec, i) -> { - if (!sec.changeBPM) { - Reflect.deleteField(sec, 'changeBPM'); - Reflect.deleteField(sec, 'bpm'); - } - if (sec.sectionNotes.length <= 0) { - Reflect.deleteField(sec, 'sectionNotes'); - } - else + song.notes.fastForEach((section, i) -> + { + Optimizer.removeDefaultValues(section, { - sec.sectionNotes.fastForEach((note, i) -> { - final type:String = Std.string(note[3]); - if (type != null) if (type.length <= 0 || type == "default") { - note.pop(); - } - }); - sec.sectionNotes.sort(sortNotes); - } + bpm: 0, + changeBPM: false, + mustHitSection: true, + sectionNotes: [], + sectionEvents: [] + }); - if (sec.sectionEvents.length <= 0 || metaClear) - Reflect.deleteField(sec, 'sectionEvents'); + if (section.sectionNotes != null) + { + section.sectionNotes.fastForEach((note, i) -> + { + Optimizer.removeDefaultValues(note, {type: "default"}); + }); - if (sec.mustHitSection) - Reflect.deleteField(sec, 'mustHitSection'); + section.sectionNotes.sort(sortNotes); + } }); - if (song.notes.length > 1) + while (true) { - while (true) { - final lastSec = song.notes[song.notes.length - 1]; - if (lastSec == null) break; - if (Reflect.fields(lastSec).length <= 0) song.notes.pop(); - else break; - } - } - - if (metaClear) { - Reflect.deleteField(song, 'offsets'); - //Reflect.deleteField(song, 'bpm'); - } - - return song; - } + var lastSection = song.notes[song.notes.length - 1]; + + if (lastSection == null || Reflect.fields(lastSection).length > 0) + break; - public static function parseJson(chartPath:String, rawJson:String = ""):SongJson - { - if (rawJson.length <= 0) { - rawJson = CoolUtil.getFileContent(chartPath).trim(); - while (!rawJson.endsWith("}")) - rawJson = rawJson.substr(0, rawJson.length - 1); + song.notes.pop(); } - return cast Json.parse(rawJson).song; + return song; } /* Use this function to get the sorted notes from a song as an array Used for pico in Stress, but you can use it on other cool stuff */ - public static function getSongNotes(diff:String, song:String):Array> + public static function getSongNotes(diff:String, song:String):Array { - final notes:Array> = []; + final notes:Array = []; loadFromFile(diff, song).notes.fastForEach((s, i) -> { if (s.sectionNotes != null) { @@ -312,8 +177,8 @@ class Song return notes; } - private static function sortNotes(note1:Array, note2:Array):Int { - return FlxSort.byValues(FlxSort.ASCENDING, note1[0], note2[0]); + private static inline function sortNotes(note1:NoteJson, note2:NoteJson):Int { + return FlxSort.byValues(FlxSort.ASCENDING, note1.time, note2.time); } public static function formatSongFolder(songName:String):String { diff --git a/source/funkin/util/song/WeekSetup.hx b/source/funkin/util/song/WeekSetup.hx index e4bfd515..5dc5f048 100644 --- a/source/funkin/util/song/WeekSetup.hx +++ b/source/funkin/util/song/WeekSetup.hx @@ -225,7 +225,8 @@ class WeekSetup } final song = PlayState.SONG; - loadScreen.setupPlay(Stage.getJson(song.stage), song.players.copy(), song.song); + final players:Array = song.players; + loadScreen.setupPlay(Stage.getJson(song.stage), players.copy(), song.song); Conductor.stop(); diff --git a/source/funkin/util/song/formats/BasicParser.hx b/source/funkin/util/song/formats/BasicParser.hx deleted file mode 100644 index b3d3dc3d..00000000 --- a/source/funkin/util/song/formats/BasicParser.hx +++ /dev/null @@ -1,78 +0,0 @@ -package funkin.util.song.formats; - -typedef BasicBpmChange = { - time:Float, - bpm:Float -} - -typedef ChartVar = { - name:String, - value:String -} - -typedef BasicSection = { - notes:Array>, - bpm:Float // set to -1 if no changes -} - -class BasicParser { - var variables:Map = []; - var bpmChanges:Array = []; - - var map:Array = []; - var fnfMap:SongJson; - - public function new() {} - - public function convertSong(path:String, diff:String):SongJson { - map = CoolUtil.getFileContent(path).split('\n'); - fnfMap = Song.getDefaultSong(); - - __initVars(); - applyVars(variables, fnfMap); - parseBpmChanges(map, bpmChanges); - - final split = path.split("/"); - fnfMap.song = split[split.length - 3]; - fnfMap.bpm = bpmChanges[0]?.bpm ?? 100.0; - - final sections = parseNotes(diff); - if (sections != null) { - for (section in sections) { - final newSec:SectionJson = Song.getDefaultSection(); - newSec.sectionNotes = section.notes; - newSec.changeBPM = section.bpm != -1; - newSec.bpm = section.bpm; - - fnfMap.notes.push(newSec); - } - } - - return fnfMap; - } - - function parseBpmChanges(map:Array, bpmChanges:Array):Void {} - - function applyVars(variables:Map, fnfMap:SongJson):Void {} - - function parseNotes(diff:String):Array { - return null; - } - - var doop:Int = 0; - - @:noCompletion - function __initVars():Void { - for (i in 0...map.length) { - final _var = __resolveVar(map[i], i); - if (_var != null) { - variables.set(variables.exists(_var.name) ? _var.name + (doop++) : _var.name, _var.value); - } - } - } - - @:noCompletion - function __resolveVar(line:String, index:Int):ChartVar { - return null; - } -} \ No newline at end of file diff --git a/source/funkin/util/song/formats/FunkinFormat.hx b/source/funkin/util/song/formats/FunkinFormat.hx deleted file mode 100644 index 72e4d9d0..00000000 --- a/source/funkin/util/song/formats/FunkinFormat.hx +++ /dev/null @@ -1,112 +0,0 @@ -package funkin.util.song.formats; - -/* - Kinda made only to not have all engine converter stuff in Song.hx -*/ - -class FunkinFormat -{ - //Fixes charts from other engines - public static function songCheck(?song:SongJson, meta:SongMeta, diff:String):SongJson - { - if (song == null) - return song; - - //Get special engine variables ready if missing - var specialFields:Array> = [ - ['players', ['bf','dad','gf']] - ]; - - for (field in specialFields) { - if (!Reflect.hasField(song, field[0])) { - Reflect.setField(song, field[0], field[1]); - } - } - - for (field in Reflect.fields(song)) { - switch (field) { - case 'gfVersion' | 'gf' | 'player3' | 'player2' | 'player1': - final playerIndex:Int = switch(field) { - case 'player1': 0; - case 'player2': 1; - default: 2; - } - final players:Array = Reflect.field(song, 'players'); - players[playerIndex] = Reflect.field(song, field); - Reflect.setField(song, 'players', players); - Reflect.deleteField(song, field); - case 'events': - final isFps = Reflect.hasField(Reflect.field(song, "events"), "events"); - song = isFps ? convertFpsChart(song) : convertPsychChart(song); - Reflect.deleteField(song, field); - } - } - return song; - } - - public static function metaCheck(?meta:SongMeta, diff:String):SongMeta { - if (meta == null) - return meta; - - return meta; - } - - // Converts FPS+ engine events - public static function convertFpsChart(song:SongJson) { - final fpsEvents:Array = Reflect.field(Reflect.field(song, "events"), "events"); - if (fpsEvents == null || fpsEvents.length <= 0) return song; - - final events:Map>> = []; - for (e in fpsEvents) { - if (!events.exists(e[0])) events.set(e[0], []); - events.get(e[0]).push([e[1], e[3], []]); - } - - for (i in events.keys()) { - Song.checkAddSections(song, i); - song.notes[i] = Song.checkSection(song.notes[i]); - for (e in events.get(i)) - song.notes[i].sectionEvents.push(e); - } - - return song; - } - - // Converts psych and forever engine events - public static function convertPsychChart(song:SongJson):SongJson { - final psychEvents:Array = Reflect.field(song, 'events'); - if (psychEvents == null || psychEvents.length <= 0) return song; - - final events:Array> = []; - for (e in psychEvents) { - final eventTime = e[0]; - final _events:Array> = e[1]; - for (i in _events) events.push([eventTime, i[0], [i[1], i[2]]]); - } - - for (i in events) { - final eventSec = Song.getTimeSection(song, i[0]); - Song.checkAddSections(song, eventSec); - song.notes[eventSec] = Song.checkSection(song.notes[eventSec]); - song.notes[eventSec].sectionEvents.push(i); - } - - return song; - } - - // Converts base game v-slice charts - public static function convertVSliceSong(song:Dynamic, diff:String):SongJson - { - for (note in cast(Reflect.field(song.notes, diff), Array)) { - - } - - - return song; - } - - public static function convertVSliceMeta(meta:Dynamic, diff:String):SongMeta - { - return meta; - } -} \ No newline at end of file diff --git a/source/funkin/util/song/formats/GhFormat.hx b/source/funkin/util/song/formats/GhFormat.hx deleted file mode 100644 index c69b0ee2..00000000 --- a/source/funkin/util/song/formats/GhFormat.hx +++ /dev/null @@ -1,97 +0,0 @@ -package funkin.util.song.formats; - -class GhFormat { - public var map:Array = []; - public function new(path:String) { - map = CoolUtil.getFileContent(path).split('\n'); - } - - public static function convertSong(path:String, ?input:GhFormat):SongJson { - var ghMap:GhFormat = input ?? new GhFormat(path); - var fnfMap:SongJson = Song.getDefaultSong(); - - final title = ghMap.getVar("Name"); - final bpmMap = ghMap.getBpmChanges(); - fnfMap.bpm = bpmMap.get(0); - - // Bpm changes crap - var _sortedChanges:Array = []; - for (i in bpmMap.keys()) { - if (i <= 0) continue; - _sortedChanges.push(i); - } - _sortedChanges.sort(function (a,b) return FlxSort.byValues(FlxSort.ASCENDING, a, b)); - - for (i in _sortedChanges) { - var _sec:Int = Song.getTimeSection(fnfMap, i)+1; - Song.checkAddSections(fnfMap, _sec); - fnfMap.notes[_sec].changeBPM = true; - fnfMap.notes[_sec].bpm = bpmMap.get(i); - } - - for (i in ghMap.getNotes()) { - fnfMap.notes[Song.getTimeSection(fnfMap, i[0])].sectionNotes.push(i); - } - - fnfMap.song = title; - - return fnfMap; - } - - public function getVar(varName:String) { - for (i in map) { - if (i.startsWith(' $varName')) { - var retVar = i.split('$varName = ')[1].trim(); - return Std.string(retVar.replace('\r','').replace('\n','').replace('"', '')); - } - } - return null; - } - - public function getBpmChanges():Map { - var bpmChanges:Map = []; - for (i in map) { - if (i.contains(" = B ")) { - var _split = i.split(" = B "); - var time = Std.parseInt(_split[0]); - var bpm = Std.parseInt(_split[1])/1000; - bpmChanges.set(time, bpm); - } - } - return bpmChanges; - } - - public function getNotes() { - var notes:Array> = []; - var _bpm = getBpmChanges().get(0); - var crochet:Float = (60 /_bpm) * 1000; - var tickToMills = (60000 / (_bpm * Std.parseInt(getVar("Resolution")))); - - for (_ in 0...map.length) { - if (map[_].startsWith("[ExpertSingle]")) { - for (n in _+2...map.length) { - if (map[n] == "}") break; - if (map[n].contains("E") || map[n].contains("S")) continue; - - final _note = map[n].trim().replace(" = N ", " "); - var note:Array = []; - for (i in _note.split(" ")) { - note.push(Std.parseInt(i)); - } - - note[0] *= tickToMills; - if (note[2] > 0) { - note[2] *= tickToMills; - note[2] -= crochet * 0.25; - } else { - note[2] = 0; - } - notes.push(note); - } - break; - } - } - - return notes; - } -} \ No newline at end of file diff --git a/source/funkin/util/song/formats/OsuFormat.hx b/source/funkin/util/song/formats/OsuFormat.hx deleted file mode 100644 index ef58f8be..00000000 --- a/source/funkin/util/song/formats/OsuFormat.hx +++ /dev/null @@ -1,99 +0,0 @@ -package funkin.util.song.formats; - -/* - Custom made osu to fnf json format for mau engin - DOES NOT CONVERT .OSZ FILES YET, ONLY .OSU CHART FILES!!!!!! -*/ - -class OsuFormat { - public var map:Array = []; - public function new(path:String) { - map = CoolUtil.getFileContent(path).split('\n'); - } - - inline public static function convertSong(path:String, ?input:OsuFormat):SongJson { - var osuMap:OsuFormat = input ?? new OsuFormat(path); - var fnfMap:SongJson = Song.getDefaultSong(); - - // Check if its not an osu!mania map - final mode = Std.parseInt(osuMap.getVar('Mode')); - if (mode != 3) - return fnfMap; - - var title = osuMap.getVar('Title'); - var version = osuMap.getVar('Version'); - var timingPoints = osuMap.getTimingPoints(); - var offset = timingPoints[0]; - var bpm = FlxMath.roundDecimal(60000 / timingPoints[1], 1); - var speed = Std.parseFloat(osuMap.getVar('OverallDifficulty')); - var hitObjects = osuMap.getHitObjects(); - - var sections:Array = []; - for (i in 0...Lambda.count(hitObjects)) { - var newSec:SectionJson = Song.getDefaultSection(); - if (hitObjects.get(i) != null) - newSec.sectionNotes = hitObjects.get(i); - sections.push(newSec); - } - - fnfMap.song = title; - fnfMap.notes = sections; - fnfMap.bpm = bpm; - fnfMap.offsets = [0,0];//[-offset,0]; - fnfMap.speed = FlxMath.roundDecimal(speed/2.5, 1); - return fnfMap; - } - - public function getVar(mapVar:String):Null { - for (line in map) { - if (line.startsWith(mapVar)) { - var retVar:String = line.split('$mapVar:')[1].trim(); - return retVar.replace('\r','').replace('\n',''); - } - } - return null; - } - - public function getTimingPoints():Array { - for (i in 0...map.length) { - if (map[i].startsWith('[TimingPoints]')) { - var returnArray:Array = []; - for (tm in map[i+1].split(',')) - returnArray.push(Std.parseFloat(tm)); - return returnArray; - } - } - return []; - } - - public function getHitObjects():Map>> { - var returnMap:Map>> = new Map>>(); - var mapCircleSize = Std.parseInt(getVar('CircleSize')); - var bpmMills = getTimingPoints()[1]; - - for (l in 0...map.length) { - if (map[l].startsWith('[HitObjects]')) { - for (i in l...map.length-1) { - if (!(map[i].length > 0 && map[i].contains(','))) continue; // Not a hit object, skip - var hitObject:Array = []; - var hitData = map[i].split(','); - for (n in 0...hitData.length) { - if (hitData[n].contains(':')) { - hitData[n] = hitData[n].split(':')[0]; - } - hitObject.push(Std.parseInt(hitData[n])); - } - var strumTime = hitObject[2]; - var noteData = Math.floor(hitObject[0] * mapCircleSize / 512); - var susLength = (hitObject[5] > 0) ? (hitObject[5] - strumTime) : 0; - var noteSec = Std.int(strumTime/(bpmMills*4)); - var noteSecArray = (returnMap.get(noteSec) != null) ? returnMap.get(noteSec) : []; - noteSecArray.push([strumTime,noteData,susLength]); - returnMap.set(noteSec, noteSecArray); - } - break; - } - } - return returnMap; - } -} \ No newline at end of file diff --git a/source/funkin/util/song/formats/QuaFormat.hx b/source/funkin/util/song/formats/QuaFormat.hx deleted file mode 100644 index 34231f47..00000000 --- a/source/funkin/util/song/formats/QuaFormat.hx +++ /dev/null @@ -1,80 +0,0 @@ -package funkin.util.song.formats; - -/* - Custom made qua (quaver) to fnf json format for mau engin - DOES NOT CONVERT .QP FILES YET, ONLY .QUA CHART FILES!!!!!! -*/ - -class QuaFormat { - public var map:Array = []; - public function new(path:String) { - map = CoolUtil.getFileContent(path).split('\n'); - } - - public static function convertSong(path:String, ?input:QuaFormat):SongJson { - var quaMap:QuaFormat = input ?? new QuaFormat(path); - var fnfMap:SongJson = Song.getDefaultSong(); - - // Check if map is above 4 keys - if (quaMap.getVar('Mode') != 'Keys4') { - return fnfMap; - } - - var title = quaMap.getVar('Title'); - var bpm = FlxMath.roundDecimal(quaMap.getVar('Bpm'), 1); - var speed = quaMap.getVar('InitialScrollVelocity'); - var hitObjects = quaMap.getHitObjects(); - - var sections:Array = []; - for (i in 0...Lambda.count(hitObjects)) { - var newSec:SectionJson = Song.getDefaultSection(); - if (hitObjects.get(i) != null) - newSec.sectionNotes = hitObjects.get(i); - sections.push(newSec); - } - - fnfMap.song = title; - fnfMap.notes = sections; - fnfMap.bpm = bpm; - fnfMap.offsets = [0,0];//[-offset,0]; - fnfMap.speed = FlxMath.roundDecimal(speed, 1); - - return fnfMap; - } - - public function getVar(mapVar:String):Dynamic { - for (line in map) { - if (line.startsWith('$mapVar: ') || line.startsWith('- $mapVar: ') || line.startsWith(' $mapVar:')) { - var retVar:String = line.split('$mapVar: ')[1].trim(); - return Std.string(retVar.replace('\r','').replace('\n','')); - } - } - return null; - } - - public function getHitObjects():Map>> { - var returnMap:Map>> = new Map>>(); - var crochet:Float = (60 / getVar('Bpm')) * 1000; - - for (l in 0...map.length) { - if (map[l].startsWith('- StartTime: ')) { - var strumTime = Std.parseInt(map[l].split('- StartTime: ')[1].trim()); - var noteData = Std.parseInt(map[l + 1].split(' Lane: ')[1].trim()) - 1; - if (noteData >= 0) { - var susLength = 0.0; - if (map[l + 2].startsWith(' EndTime: ')) { // Get sus length if variable exists - susLength = Std.parseInt(map[l + 2].split(' EndTime: ')[1].trim()) - strumTime; - susLength -= crochet / 4; - } - - var noteSec = Std.int(strumTime/(crochet*4)); - var noteSecArray = (returnMap.get(noteSec) != null) ? returnMap.get(noteSec) : []; - noteSecArray.push([strumTime,noteData,susLength]); - returnMap.set(noteSec, noteSecArray); - } - } - } - - return returnMap; - } -} \ No newline at end of file diff --git a/source/funkin/util/song/formats/SmFormat.hx b/source/funkin/util/song/formats/SmFormat.hx deleted file mode 100644 index 318fdfcb..00000000 --- a/source/funkin/util/song/formats/SmFormat.hx +++ /dev/null @@ -1,167 +0,0 @@ -package funkin.util.song.formats; - -import funkin.util.song.formats.BasicParser.ChartVar; -import funkin.util.song.formats.BasicParser.BasicSection; -import funkin.util.song.formats.BasicParser.BasicBpmChange; - -/** - * Custom made sm (stepmania) to fnf json format for mau engin - * @author maybemaru - */ - -class SmFormat extends BasicParser { - public static function convert(path:String, diff:String):SongJson { - return new SmFormat().convertSong(path, diff); - } - - override function applyVars(variables:Map, fnfMap:SongJson) { - fnfMap.offsets = [Std.int(Std.parseFloat(variables.get('OFFSET')) * -1000), 0]; - fnfMap.speed = 2.5; - } - - override function parseBpmChanges(map:Array, bpmChanges:Array) { - for (i in variables.get("BPMS").split(",")) { - final data = i.split("="); - bpmChanges.push({ - time: Std.parseFloat(data[0]) * 0.25, // Calculated in measures - bpm: Std.parseFloat(data[1]) - }); - } - } - - override function __resolveVar(line:String, index:Int):ChartVar { - if (line.trim().startsWith("#")) { - var varName:String = line.split(":")[0]; - varName = varName.substring(1, varName.length); - - var retVar:String = line.split('$varName:')[1].trim().replace('\r','').replace('\n',''); - if (!retVar.endsWith(";")) { - index++; - while (!map[index].endsWith(";") && !map[index].startsWith(";")) { - var lineValue = map[index].trim().replace('\r','').replace('\n',''); - if (!lineValue.contains("// measure")) retVar += lineValue; // I have no idea why this exists, only makes parsing harder - else retVar += ","; - - if (lineValue.length == 4) { - retVar += "-"; - } - - index++; - - if (index > map.length) { - throw("Couldnt get variable for " + varName); - break; - } - } - } - - return { - name: varName, - value: (retVar.endsWith(';')) ? retVar.substring(0, retVar.length - 1) : retVar - } - } - - return null; - } - - var foundDiffs:Array = []; - - override function parseNotes(diff:String):Array { - var baseNotes:Array = []; - for (name => variable in variables) { - if (name.startsWith("NOTES")) { - baseNotes.push(variable); - } - } - - for (chart in baseNotes) { - var parts = chart.split(":"); - var chartDiff:String = parts[2].toLowerCase(); - foundDiffs.push(chartDiff); - - if (diff == chartDiff) { - return parseSm(parts[5].substr(0, parts[5].length - 1)); - } - } - - throw("Couldn't find StepMania chart for difficulty " + diff + "\nFound difficulties " + foundDiffs.toString()); - return null; - } - - function parseSm(notes:String):Array { - // For ease of use, diving it - var sections:Array> = []; - for (sec in notes.split(",")) { - sections.push(sec.split("-")); - } - - var sectionsVector:Array = []; - for (i in 0...sections.length) { - sectionsVector.push({ - notes: [], - bpm: -1 - }); - } - - var curBpm:Float = bpmChanges[0].bpm; - bpmChanges.remove(bpmChanges[0]); - - var position:Float = 0.0; - var measurePosition:Float = -1.0; - - var crochet:Float = 0.0; - - var recalc = function (index:Int) { - crochet = (60 / curBpm) * (4 / sections[index].length) * 1000; - } - - for (i in 0...sections.length) { - recalc(i); - - for (l in 0...sections[i].length) { - //Check for bpm changes - if (bpmChanges[0] != null) { - while (bpmChanges[0].time <= measurePosition) { - curBpm = bpmChanges[0].bpm; - sectionsVector[i].bpm = curBpm; - recalc(i); - - bpmChanges.remove(bpmChanges[0]); - if (bpmChanges[0] == null) break; - } - } - - // Add line notes - var lineSplit = sections[i][l].split(""); - for (n in 0...lineSplit.length) { - switch (lineSplit[n]) { - case '1':// Normal note - sectionsVector[i].notes.push([position, n, 0.0]); - case '2':// Hold head - sectionsVector[i].notes.push([position, n, resolveSustain(sections[i], l, n, crochet)]); - case '4':// Roll head - sectionsVector[i].notes.push([position, n, resolveSustain(sections[i], l, n, crochet), "roll"]); - case 'M':// Mine - sectionsVector[i].notes.push([position, n, 0.0, "mine"]); - default: - } - } - - // Move conductor - position += crochet; - measurePosition += 1 / sections[i].length; - } - } - - return sectionsVector; - } - - function resolveSustain(measure:Array, line:Int, index:Int, crochet:Float):Float { - var length:Int = 1; - for (i in line...measure.length) { - if (measure[i].split("")[index] == "3") return length * crochet; - length++; - } - return 0.0; - } -} \ No newline at end of file