189 lines
6.7 KiB
Haxe
189 lines
6.7 KiB
Haxe
package funkin.mods;
|
|
|
|
import openfl.utils.Assets;
|
|
import openfl.utils.AssetManifest;
|
|
import openfl.utils.AssetLibrary;
|
|
import flixel.graphics.FlxGraphic;
|
|
|
|
#if MOD_SUPPORT
|
|
import sys.FileSystem;
|
|
#end
|
|
|
|
import flixel.FlxG;
|
|
import flixel.FlxState;
|
|
import haxe.io.Path;
|
|
|
|
using StringTools;
|
|
|
|
class ModsFolder {
|
|
#if MOD_SUPPORT
|
|
/**
|
|
* Last time the folder was modified.
|
|
*/
|
|
public static var lastFolderEditTime:Date = null;
|
|
#end
|
|
|
|
/**
|
|
* Current mod folder. Will affect `Paths`.
|
|
*/
|
|
public static var currentModFolder:String = null;
|
|
/**
|
|
* Array of all loaded mods' folder names.
|
|
*/
|
|
public static var loadedMods:Array<String> = [];
|
|
/**
|
|
* Path to the `mods` folder.
|
|
*/
|
|
public static var modsPath:String = "./mods/";
|
|
|
|
/**
|
|
* Initialises `mods` folder by adding callbacks and such.
|
|
*/
|
|
public static function init() {
|
|
FlxG.signals.preStateCreate.add(onStateSwitch);
|
|
}
|
|
|
|
/**
|
|
* Loads a mod with the specified name.
|
|
* @param modName Name of the mod
|
|
* @param force Whenever the mod should be reloaded if it has already been loaded
|
|
*/
|
|
public static function loadMod(mod:String, force:Bool = false) {
|
|
if (FileSystem.exists('${modsPath}$mod.zip')) {
|
|
var e = loadLibraryFromZip('mods/$mod'.toLowerCase(), '${modsPath}$mod.zip', force);
|
|
loadedMods.push(mod);
|
|
return e;
|
|
} else {
|
|
var e = loadLibraryFromFolder('mods/$mod'.toLowerCase(), '${modsPath}$mod', force);
|
|
loadedMods.push(mod);
|
|
return e;
|
|
}
|
|
}
|
|
|
|
public static function prepareLibrary(libName:String, force:Bool = false) {
|
|
if (Assets.hasLibrary(libName)) {
|
|
if (force)
|
|
Assets.unloadLibrary(libName);
|
|
else
|
|
return null;
|
|
}
|
|
|
|
var assets:AssetManifest = new AssetManifest();
|
|
assets.name = libName;
|
|
assets.version = 2;
|
|
assets.libraryArgs = [];
|
|
assets.assets = [];
|
|
|
|
return AssetLibrary.fromManifest(assets);
|
|
}
|
|
|
|
public static function loadLibraryFromZip(libName:String, zipPath:String, force:Bool = false) {
|
|
var lib = prepareLibrary(libName, force);
|
|
@:privateAccess
|
|
lib.__proxy = new ZipFolderLibrary(zipPath, libName);
|
|
Assets.registerLibrary(libName, lib);
|
|
return lib;
|
|
}
|
|
|
|
public static function loadLibraryFromFolder(libName:String, folder:String, force:Bool = false) {
|
|
var lib = prepareLibrary(libName, force);
|
|
@:privateAccess
|
|
lib.__proxy = new ModsFolderLibrary(folder, libName);
|
|
Assets.registerLibrary(libName, lib);
|
|
return lib;
|
|
}
|
|
|
|
/**
|
|
* Gets all assets in folders and put them in the `assets` array.
|
|
* @param assets Array of assets
|
|
* @param rootPath Root path
|
|
* @param path Path inside of the rootPath (ex: `root path/path/path2/`)
|
|
* @param libraryName Name of the library (ex: `mods/my mod/`)
|
|
* @param prefix Prefix for the asset names (ex: `assets/mods/my mod/`)
|
|
* @param addRoot Whenever the root should be added to the paths. Defaults to false
|
|
*/
|
|
public static function getAssetFiles(assets:Array<Dynamic>, rootPath:String, path:String, libraryName:String, prefix:String = "", addRoot:Bool = false) {
|
|
#if sys
|
|
for(f in FileSystem.readDirectory('$rootPath$path')) {
|
|
if (FileSystem.isDirectory('$rootPath$path$f')) {
|
|
// fuck you git
|
|
if (f.toLowerCase() != ".git")
|
|
getAssetFiles(assets, rootPath, '$path$f/', libraryName);
|
|
} else {
|
|
var type = "BINARY";
|
|
var useExt:Bool = true;
|
|
switch(Path.extension(f).toLowerCase()) {
|
|
case "txt" | "xml" | "json" | "hx" | "hscript" | "hsc" | "lua" | "frag" | "vert":
|
|
type = "TEXT";
|
|
case "png":
|
|
type = "IMAGE";
|
|
case "ogg":
|
|
type = path.toLowerCase().startsWith("music") ? "MUSIC" : "SOUND";
|
|
case "ttf":
|
|
type = "FONT";
|
|
useExt = false;
|
|
|
|
}
|
|
var stat = FileSystem.stat('$rootPath$path$f');
|
|
assets.push({
|
|
type: type,
|
|
id: ('assets/$libraryName/$prefix$path${useExt ? f : Path.withoutExtension(f)}').toLowerCase(), // for case sensitive shit & correct linux support
|
|
path: (addRoot ? rootPath : '') + '$path$f',
|
|
size: stat.size,
|
|
edited: stat.mtime.getTime() / 1000
|
|
});
|
|
}
|
|
}
|
|
#end
|
|
}
|
|
|
|
private static function onStateSwitch(newState:FlxState) {
|
|
Assets.cache.clear();
|
|
lime.utils.Assets.cache.clear();
|
|
|
|
#if MOD_SUPPORT
|
|
if (currentModFolder == null) return;
|
|
var bmapsToRemove:Array<FlxGraphic> = [];
|
|
@:privateAccess
|
|
for(bmap in FlxG.bitmap._cache) {
|
|
if (bmap.assetsKey != null) {
|
|
var e = new LimeLibrarySymbol(bmap.assetsKey);
|
|
if (e.library is openfl.utils.AssetLibrary) {
|
|
@:privateAccess
|
|
e.library = cast(e.library, openfl.utils.AssetLibrary).__proxy;
|
|
}
|
|
if (e.library is ModsAssetLibrary) {
|
|
var lib = cast(e.library, ModsAssetLibrary);
|
|
if (!lib.__parseAsset(e.symbolName)) continue;
|
|
if (!lib.__isCacheValid(e.library.cachedImages, lib._parsedAsset)) {
|
|
e.library.cachedImages.remove(lib._parsedAsset);
|
|
bmapsToRemove.push(bmap);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: add setting for cache clearing
|
|
@:privateAccess
|
|
for(libName=>lib in lime.utils.Assets.libraries) {
|
|
var library = lib;
|
|
if (library is openfl.utils.AssetLibrary) {
|
|
var flLib = cast(library, openfl.utils.AssetLibrary);
|
|
@:privateAccess
|
|
if (flLib.__proxy != null) library = flLib.__proxy;
|
|
}
|
|
if (library is ModsAssetLibrary) {
|
|
var modLib = cast(library, ModsAssetLibrary);
|
|
@:privateAccess
|
|
library.cachedBytes = [];
|
|
@:privateAccess
|
|
for(sound in library.cachedAudioBuffers)
|
|
sound.dispose();
|
|
|
|
}
|
|
}
|
|
for(e in bmapsToRemove)
|
|
FlxG.bitmap.remove(e);
|
|
#end
|
|
}
|
|
} |