diff --git a/source/funkin/backend/system/Flags.hx b/source/funkin/backend/system/Flags.hx index eace974c..934919f1 100644 --- a/source/funkin/backend/system/Flags.hx +++ b/source/funkin/backend/system/Flags.hx @@ -1,9 +1,11 @@ package funkin.backend.system; import flixel.util.FlxColor; +import funkin.backend.assets.ModsFolder; import funkin.backend.assets.IModsAssetLibrary; import funkin.backend.assets.ScriptedAssetLibrary; import funkin.backend.system.macros.GitCommitMacro; +import funkin.backend.utils.IniUtil; import lime.app.Application; import lime.utils.AssetLibrary as LimeAssetLibrary; import lime.utils.AssetType; @@ -13,7 +15,26 @@ import lime.utils.AssetType; */ @:build(funkin.backend.system.macros.FlagMacro.build()) class Flags { + // -- Codename's Addon Config -- + @:bypass public static var addonFlags:Map = []; + + // -- Codename's Mod Config -- + public static var MOD_NAME:String = ""; + public static var MOD_DESCRIPTION:String = ""; + public static var MOD_API_VERSION:Int = 1; + public static var MOD_DOWNLOAD_LINK:String = ""; + public static var MOD_DEPENDENCIES:Array = []; + + public static var MOD_ICON64:String = ""; + public static var MOD_ICON32:String = ""; + public static var MOD_ICON16:String = ""; + public static var MOD_ICON:String = ""; + + public static var MOD_DISCORD_CLIENT_ID:String = ""; + public static var MOD_DISCORD_LOGO_KEY:String = ""; + public static var MOD_DISCORD_LOGO_TEXT:String = ""; // -- Codename's Default Flags -- + public static var CURRENT_API_VERSION:Int = 1; public static var COMMIT_NUMBER:Int = GitCommitMacro.commitNumber; public static var COMMIT_HASH:String = GitCommitMacro.commitHash; public static var COMMIT_MESSAGE:String = 'Commit $COMMIT_NUMBER ($COMMIT_HASH)'; @@ -159,29 +180,9 @@ class Flags { @:bypass public static var customFlags:Map = []; public static function loadFromData(flags:Map, data:String) { - var trimmed:String; - var splitContent = [for(e in data.split("\n")) if ((trimmed = e.trim()) != "") trimmed]; + var res = IniUtil.parseString(data); - for(line in splitContent) { - if(line.startsWith(";")) continue; - if(line.startsWith("#")) continue; - if(line.startsWith("//")) continue; - if(line.length == 0) continue; - if(line.charAt(0) == "[" && line.charAt(line.length-1) == "]") continue; - - var index = line.indexOf("="); - if(index == -1) continue; - var name = line.substr(0, index).trim(); - var value = line.substr(index+1).trim(); - - var wasQuoted = value.length > 1 && value.charCodeAt(0) == '"'.code && value.charCodeAt(value.length-1) == '"'.code; - if(wasQuoted) value = value.substr(1, value.length - 2); - if((!wasQuoted && value.length == 0) || name.length == 0) - continue; - - if(!flags.exists(name)) - flags[name] = value; - } + for (section in res) for (key => value in section) flags[key] = value; } public static function loadFromDatas(datas:Array) { @@ -205,16 +206,42 @@ class Flags { * Loads the flags from the assets. **/ public static function load(?libs:Array = null) { - if (libs == null) - libs = Paths.assetsTree.libraries; - final flagsPath = Paths.getPath("flags.ini"); - var datas:Array = [ - for(lib in libs) - if(lib.exists(flagsPath, AssetType.TEXT)) - lib.getAsset(flagsPath, AssetType.TEXT) - ]; + if (libs == null) { + libs = Paths.assetsTree.libraries.copy(); + libs.reverse(); + } + for(lib in libs) { + var l = lib; + if (l is openfl.utils.AssetLibrary) { + @:privateAccess + l = cast(l, openfl.utils.AssetLibrary).__proxy; + } - var flags:Map = loadFromDatas(datas); - parseFlags(flags); + if (l is IModsAssetLibrary) { + var flagsTxt = ""; + if (l.exists(Paths.ini("config/modpack"), AssetType.TEXT)) + flagsTxt = l.getAsset(Paths.ini("config/modpack"), AssetType.TEXT); + if (cast(l, IModsAssetLibrary).modName == "assets") continue; + + if (cast(l, IModsAssetLibrary).modName == ModsFolder.currentModFolder) { + var flags:Map = []; + loadFromData(flags, flagsTxt); + parseFlags(flags); + } + else { + var flags:Map = []; + loadFromData(flags, flagsTxt); + addonFlags.set(cast(l, IModsAssetLibrary).modName.toLowerCase().replace(" ", "").trim(), flags); + } + } + else { + var flagsTxt = ""; + if (l.exists(Paths.getPath("data/config/flags.ini"), AssetType.TEXT)) + flagsTxt = l.getAsset(Paths.getPath("data/config/flags.ini"), AssetType.TEXT); + var flags:Map = []; + loadFromData(flags, flagsTxt); + parseFlags(flags); + } + } } } \ No newline at end of file diff --git a/source/funkin/backend/system/MainState.hx b/source/funkin/backend/system/MainState.hx index 1f48d11e..bb734a22 100644 --- a/source/funkin/backend/system/MainState.hx +++ b/source/funkin/backend/system/MainState.hx @@ -5,8 +5,10 @@ import sys.FileSystem; #end import flixel.FlxState; import funkin.backend.assets.ModsFolder; +import funkin.backend.assets.ModsFolderLibrary; import funkin.backend.chart.EventsData; import funkin.backend.system.framerate.Framerate; +import funkin.editors.ModConfigWarning; import funkin.menus.TitleState; import haxe.io.Path; @@ -112,10 +114,23 @@ class MainState extends FlxState { if (Framerate.isLoaded) Framerate.instance.reload(); - FlxG.switchState(new TitleState()); - #if sys CoolUtil.safeAddAttributes('./.temp/', NativeAPI.FileAttribute.HIDDEN); #end + + if (Options.devMode) { + var lib:ModsFolderLibrary; + for (e in Paths.assetsTree.libraries) { + @:privateAccess if (!(e is openfl.utils.AssetLibrary) || !((lib = cast cast(e, openfl.utils.AssetLibrary).__proxy) is ModsFolderLibrary)) continue; + if (lib.modName == ModsFolder.currentModFolder) { + if (lib.exists(Paths.ini("config/modpack"), lime.utils.AssetType.TEXT)) break; + + FlxG.switchState(new ModConfigWarning(lib)); + return; + } + } + } + + FlxG.switchState(new TitleState()); } } \ No newline at end of file diff --git a/source/funkin/backend/utils/CoolUtil.hx b/source/funkin/backend/utils/CoolUtil.hx index 2c420393..f6fe28a2 100644 --- a/source/funkin/backend/utils/CoolUtil.hx +++ b/source/funkin/backend/utils/CoolUtil.hx @@ -422,33 +422,24 @@ class CoolUtil FlxG.sound.music.persist = Persist; FlxG.sound.defaultMusicGroup.add(FlxG.sound.music); - var infoPath = '${Path.withoutExtension(path)}.ini'; - if (Assets.exists(infoPath)) { - var musicInfo = IniUtil.parseAsset(infoPath, [ - "BPM" => null, - "TimeSignature" => Flags.DEFAULT_BEATS_PER_MEASURE + "/" + Flags.DEFAULT_STEPS_PER_BEAT, - "LoopTime" => '${Flags.DEFAULT_LOOP_TIME}', - "EndTime" => null, - "Offset" => '0' - ]); + var iniPath = '${Path.withoutExtension(path)}.ini'; + var musicData = Assets.exists(iniPath) ? IniUtil.parseAsset(iniPath)["Global"] : null; + if (musicData != null) { + if (musicData["LoopTime"] != null) FlxG.sound.music.loopTime = Std.parseFloat(musicData["LoopTime"]) * 1000; + if (musicData["EndTime"] != null) FlxG.sound.music.endTime = Std.parseFloat(musicData["EndTime"]) * 1000; + if (musicData["Offset"] != null) FlxG.sound.music.endTime = Std.parseFloat(musicData["Offset"]) * 1000; - FlxG.sound.music.loopTime = Std.parseFloat(musicInfo["LoopTime"]) * 1000; - if (musicInfo["EndTime"] != null) FlxG.sound.music.endTime = Std.parseFloat(musicInfo["EndTime"]) * 1000; - FlxG.sound.music.offset = Std.parseFloat(musicInfo["Offset"]) * 1000; - - var timeSignParsed:Array> = musicInfo["TimeSignature"] == null ? [] : [for(s in musicInfo["TimeSignature"].split("/")) Std.parseFloat(s)]; - var beatsPerMeasure:Float = Flags.DEFAULT_BEATS_PER_MEASURE; - var stepsPerBeat:Int = Flags.DEFAULT_STEPS_PER_BEAT; - - // Check later, i don't think timeSignParsed can contain null, only nan - if (timeSignParsed.length == 2 && !timeSignParsed.contains(null)) { - beatsPerMeasure = timeSignParsed[0] == null || timeSignParsed[0] <= 0 ? Flags.DEFAULT_BEATS_PER_MEASURE : cast timeSignParsed[0]; - stepsPerBeat = timeSignParsed[1] == null || timeSignParsed[1] <= 0 ? Flags.DEFAULT_STEPS_PER_BEAT : cast timeSignParsed[1]; + var timeSignParsed:Array> = musicData["TimeSignature"] == null ? [] : [for(s in musicData["TimeSignature"].split("/")) Std.parseFloat(s)]; + var beatsPerMeasure:Float = Flags.DEFAULT_BEATS_PER_MEASURE, stepsPerBeat:Float = Flags.DEFAULT_STEPS_PER_BEAT; + if (timeSignParsed.length == 2) { + beatsPerMeasure = Math.isNaN(timeSignParsed[0]) ? Flags.DEFAULT_BEATS_PER_MEASURE : timeSignParsed[0]; + stepsPerBeat = Math.isNaN(timeSignParsed[1]) ? Flags.DEFAULT_STEPS_PER_BEAT : (16 / timeSignParsed[1]); // from denominator } - var bpm:Null = Std.parseFloat(musicInfo["BPM"]).getDefault(DefaultBPM); - Conductor.changeBPM(bpm, beatsPerMeasure, stepsPerBeat); - } else + var bpm:Float = Std.parseFloat(musicData["BPM"]).getDefault(DefaultBPM); + Conductor.changeBPM(bpm, beatsPerMeasure, Math.floor(stepsPerBeat)); + } + else Conductor.changeBPM(DefaultBPM); FlxG.sound.music.play(); diff --git a/source/funkin/backend/utils/DiscordUtil.hx b/source/funkin/backend/utils/DiscordUtil.hx index 39a92e49..68bdf128 100644 --- a/source/funkin/backend/utils/DiscordUtil.hx +++ b/source/funkin/backend/utils/DiscordUtil.hx @@ -89,6 +89,10 @@ class DiscordUtil config.logoKey = config.logoKey.getDefault(Flags.DEFAULT_DISCORD_LOGO_KEY); config.logoText = config.logoText.getDefault(Flags.DEFAULT_DISCORD_LOGO_TEXT); config.clientID = config.clientID.getDefault(Flags.DEFAULT_DISCORD_CLIENT_ID); + + if (Flags.MOD_DISCORD_CLIENT_ID.length > 0) config.clientID = Flags.MOD_DISCORD_CLIENT_ID; + if (Flags.MOD_DISCORD_LOGO_KEY.length > 0) config.logoKey = Flags.MOD_DISCORD_LOGO_KEY; + if (Flags.MOD_DISCORD_LOGO_TEXT.length > 0) config.logoText = Flags.MOD_DISCORD_LOGO_TEXT; currentID = config.clientID; #end } diff --git a/source/funkin/backend/utils/IniUtil.hx b/source/funkin/backend/utils/IniUtil.hx index 5de61303..22cecee4 100644 --- a/source/funkin/backend/utils/IniUtil.hx +++ b/source/funkin/backend/utils/IniUtil.hx @@ -1,39 +1,33 @@ package funkin.backend.utils; -/** - * DOESN'T SUPPORT CATEGORIES YET!! - */ class IniUtil { - public static inline function parseAsset(assetPath:String, ?defaultVariables:Map) - return parseString(Assets.getText(assetPath), defaultVariables); + public static inline function parseAsset(assetPath:String) + return parseString(Assets.getText(assetPath)); - public static function parseString(data:String, ?defaultVariables:Map) { - var trimmed:String; - var splitContent = [for(e in data.split("\n")) if ((trimmed = e.trim()) != "") trimmed]; + public static function parseString(data:String):Map> { + var res:Map> = []; + parseStringToMap(res, data); + return res; + } - var finalMap:Map = []; - if (defaultVariables != null) - for(k=>e in defaultVariables) - finalMap[k] = e; + public static function parseStringToMap(map:Map>, data:String) { + var section:Map = null; - for(line in splitContent) { - // comment - if (line.startsWith(";")) continue; - // categories; not supported yet - if (line.startsWith("[") && line.endsWith("]")) continue; + var regexSec = ~/\\[(.+)\\]/g, regexVal = ~/(.+)=(.+)/g, quote = ~/[\\'"](.+)[\\'"]/g, iln = 0; + do { + var line = data.substring(iln, ((iln = data.indexOf('\n', iln) + 1) == 0 ? data.length : iln - 1)); + if (regexSec.match(line)) { + if ((section = map.get(regexSec.matched(1))) == null) map.set(regexSec.matched(1), section = []); + } + else if (regexVal.match(line)) { + var s = regexVal.matched(2).trim(); + if (quote.match(s)) s = quote.matched(1); - var index = line.indexOf("="); - var name = line.substr(0, index).trim(); - var value = line.substr(index+1).trim(); - - if (value.length > 1 && value.charCodeAt(0) == '"'.code && value.charCodeAt(value.length-1) == '"'.code) - value = value.substr(1, value.length - 2); - - if (value.length == 0 || name.length == 0) - continue; - - finalMap[name] = value; - } - return finalMap; + if (section == null) { + if ((section = map.get("Global")) == null) map.set("Global", section = []); + } + section.set(regexVal.matched(1).trim(), s); + } + } while (iln != 0); } } \ No newline at end of file diff --git a/source/funkin/editors/ModConfigWarning.hx b/source/funkin/editors/ModConfigWarning.hx new file mode 100644 index 00000000..ca1fc34f --- /dev/null +++ b/source/funkin/editors/ModConfigWarning.hx @@ -0,0 +1,75 @@ +package funkin.editors; + +import funkin.backend.assets.ModsFolderLibrary; +import flixel.FlxState; + +class ModConfigWarning extends UIState { + public static var hadPopup:Bool = false; + + var library:ModsFolderLibrary = null; + var goToState:Class; + + public static inline var defaultModConfigText = +'[Common] +MOD_NAME="YOUR MOD NAME HERE" +MOD_DESCRIPTION="YOUR MOD DESCRIPTION HERE" + +# DO NOT EDIT!! this is used to check for version compatibility! +MOD_API_VERSION=1 + +MOD_DOWNLOAD_LINK="YOUR MOD PAGE LINK HERE" + +# Not supported yet +;MOD_ICON64="path/to/icon64.png" +;MOD_ICON32="path/to/icon32.png" +;MOD_ICON16="path/to/icon16.png" +;MOD_ICON="path/to/icon.ico" + +[Flags] +DISABLE_BETA_WARNING_SCREEN=true + +[Discord] +MOD_DISCORD_CLIENT_ID="" +MOD_DISCORD_LOGO_KEY="" +MOD_DISCORD_LOGO_TEXT=""'; + + public function new(library:ModsFolderLibrary, ?goToState:Class) { + super(); + this.library = library; + this.goToState = goToState != null ? goToState : funkin.menus.TitleState; + } + + override function createPost() { + super.createPost(); + hadPopup = true; + + var substate = new UIWarningSubstate("Missing mod folder configuration!", "Your mod is currently missing a mod config file!\n\n\nWould you like to automatically generate one?\n\n(PS: If this is not your mod please disable Developer mode to stop this popup from appearing.)", [ + { + label: "Not Now", + color: 0x969533, + onClick: function (_) { + MusicBeatState.skipTransOut = MusicBeatState.skipTransIn = false; + FlxG.switchState(cast Type.createInstance(goToState, [])); + } + }, + { + label: "Yes", + onClick: function(_) { + var path = '${library.folderPath}/data/config/modpack.ini'; + CoolUtil.safeSaveFile(path, defaultModConfigText); + openSubState(new UIWarningSubstate("Created mod config!", "Your mod config file has been created at " + path + "!", [ + { + label: "Ok", + onClick: function (_) { + MusicBeatState.skipTransOut = MusicBeatState.skipTransIn = false; + FlxG.switchState(cast Type.createInstance(goToState, [])); + } + }, + ], false)); + } + } + ], false); + substate.bHeight = 300; + openSubState(substate); + } +} \ No newline at end of file diff --git a/source/funkin/editors/character/CharacterEditor.hx b/source/funkin/editors/character/CharacterEditor.hx index 9a4fa977..f9cb45d3 100644 --- a/source/funkin/editors/character/CharacterEditor.hx +++ b/source/funkin/editors/character/CharacterEditor.hx @@ -361,8 +361,19 @@ class CharacterEditor extends UIState { if (charXML.exists("flipX") && !character.flipX) charXML.remove("flipX"); if (charXML.exists("scale") && character.scale.x == 1) charXML.remove("scale"); if (charXML.exists("antialiasing") && character.antialiasing) charXML.remove("antialiasing"); + if (charXML.exists("sprite") && character.sprite == character.curCharacter) charXML.remove("sprite"); + if (charXML.exists("icon") && character.icon == character.curCharacter) charXML.remove("icon"); - return "\n" + Printer.print(charXML, true); + for (a in charXML.elements()) + if (a.nodeName == "anim") { + if (a.exists("x") && a.get("x") == "0") a.remove("x"); + if (a.exists("y") && a.get("y") == "0") a.remove("y"); + if (a.exists("fps") && a.get("fps") == "24") a.remove("fps"); + if (a.exists("loop") && a.get("loop") == "false") a.remove("loop"); + } + + var xmlThingYea:String = "\n" + Printer.print(charXML, Options.editorPrettyPrint); + return Options.editorPrettyPrint ? xmlThingYea : xmlThingYea.replace("\n", ""); } function _edit_undo(_) { @@ -654,4 +665,4 @@ enum CharacterChange { CDeleteAnim(animID:Int, animData:AnimData); CChangeOffset(name:String, change:FlxPoint); CResetOffsets(oldOffsets:Map); -} \ No newline at end of file +} diff --git a/source/funkin/editors/ui/UIWarningSubstate.hx b/source/funkin/editors/ui/UIWarningSubstate.hx index 4f4a0380..b29b6bce 100644 --- a/source/funkin/editors/ui/UIWarningSubstate.hx +++ b/source/funkin/editors/ui/UIWarningSubstate.hx @@ -32,6 +32,8 @@ class UIWarningSubstate extends MusicBeatSubstate { var warnCam:FlxCamera; + public var bHeight:Int = 232; + public override function onSubstateOpen() { super.onSubstateOpen(); parent.persistentUpdate = false; @@ -69,7 +71,7 @@ class UIWarningSubstate extends MusicBeatSubstate { warnCam.zoom = 0.1; FlxG.cameras.add(warnCam, false); - var spr = new UISliceSprite(0, 0, CoolUtil.maxInt(560, 30 + (170 * buttons.length)), 232, 'editors/ui/${isError ? "normal" : "grayscale"}-popup'); + var spr = new UISliceSprite(0, 0, CoolUtil.maxInt(560, 30 + (170 * buttons.length)), bHeight, 'editors/ui/${isError ? "normal" : "grayscale"}-popup'); spr.x = (FlxG.width - spr.bWidth) / 2; spr.y = (FlxG.height - spr.bHeight) / 2; spr.color = isError ? 0xFFFF0000 : 0xFFFFFF00; diff --git a/source/funkin/menus/MainMenuState.hx b/source/funkin/menus/MainMenuState.hx index 220bf73b..3ae6be83 100644 --- a/source/funkin/menus/MainMenuState.hx +++ b/source/funkin/menus/MainMenuState.hx @@ -29,6 +29,7 @@ class MainMenuState extends MusicBeatState override function create() { + super.create(); DiscordUtil.call("onMenuLoaded", ["Main Menu"]); diff --git a/source/funkin/options/Options.hx b/source/funkin/options/Options.hx index 63e791f5..9296b00f 100644 --- a/source/funkin/options/Options.hx +++ b/source/funkin/options/Options.hx @@ -128,9 +128,9 @@ class Options // Misc public static var P2_RESET:Array = []; public static var P2_SWITCHMOD:Array = []; - public static var P2_VOLUME_UP:Array = [NUMPADPLUS]; - public static var P2_VOLUME_DOWN:Array = [NUMPADMINUS]; - public static var P2_VOLUME_MUTE:Array = [NUMPADZERO]; + public static var P2_VOLUME_UP:Array = [PLUS]; + public static var P2_VOLUME_DOWN:Array = [MINUS]; + public static var P2_VOLUME_MUTE:Array = [ZERO]; // Debugs public static var P2_DEV_ACCESS:Array = [];