Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions source/funkin/backend/assets/AssetsLibraryList.hx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,15 @@ import funkin.backend.assets.IModsAssetLibrary;
import lime.utils.AssetLibrary;

class AssetsLibraryList extends AssetLibrary {

public var libraries:Array<AssetLibrary> = [];

// is true if any library in `libraries` contains some kind of compressed library.
public var hasCompressedLibrary(get, never):Bool;
function get_hasCompressedLibrary():Bool {
for (l in libraries) if (getCleanLibrary(l).isCompressed) return true;
return false;
}

@:allow(funkin.backend.system.Main)
@:allow(funkin.backend.system.MainState)
Expand Down Expand Up @@ -141,9 +149,7 @@ class AssetsLibraryList extends AssetLibrary {
public override inline function getAsset(id:String, type:String):Dynamic
return getSpecificAsset(id, type, BOTH);

public override function isLocal(id:String, type:String) {
return true;
}
public override function isLocal(id:String, type:String) return true;

public function new(?base:AssetLibrary) {
super();
Expand Down
30 changes: 10 additions & 20 deletions source/funkin/backend/assets/ModsFolder.hx
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,11 @@ class ModsFolder {
*/
public static function loadModLib(path:String, force:Bool = false, ?modName:String) {
#if MOD_SUPPORT
if (FileSystem.exists('$path.zip'))
return loadLibraryFromZip('$path'.toLowerCase(), '$path.zip', force, modName);
else
return loadLibraryFromFolder('$path'.toLowerCase(), '$path', force, modName);
for (ext in Flags.ALLOWED_ZIP_EXTENSIONS) {
if (!FileSystem.exists('$path.$ext')) continue;
return loadLibraryFromZip('$path'.toLowerCase(), '$path.$ext', force, modName);
}
return loadLibraryFromFolder('$path'.toLowerCase(), '$path', force, modName);

#else
return null;
Expand All @@ -96,27 +97,16 @@ class ModsFolder {
public static function getModsList():Array<String> {
var mods:Array<String> = [];
#if MOD_SUPPORT
if (!FileSystem.exists(modsPath)) {
// Mods directory does not exist yet, create it
FileSystem.createDirectory(modsPath);
}
// Mods directory does not exist yet, create it
if (!FileSystem.exists(modsPath)) FileSystem.createDirectory(modsPath);

final modsList:Array<String> = FileSystem.readDirectory(modsPath);

if (modsList == null || modsList.length <= 0)
return mods;
if (modsList == null || modsList.length <= 0) return mods;

for (modFolder in modsList) {
if (FileSystem.isDirectory(modsPath + modFolder)) {
mods.push(modFolder);
} else {
var ext = Path.extension(modFolder).toLowerCase();
switch(ext) {
case 'zip':
// is a zip mod!!
mods.push(Path.withoutExtension(modFolder));
}
}
if (FileSystem.isDirectory(modsPath + modFolder)) mods.push(modFolder);
else if (Flags.ALLOWED_ZIP_EXTENSIONS.contains(Path.extension(modFolder))) mods.push(Path.withoutExtension(modFolder));
}
#end
return mods;
Expand Down
60 changes: 43 additions & 17 deletions source/funkin/backend/assets/ZipFolderLibrary.hx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package funkin.backend.assets;

import funkin.backend.system.Flags;

import haxe.io.Path;
import lime.graphics.Image;
import lime.media.AudioBuffer;
import lime.text.Font;
import lime.utils.Bytes;
import openfl.utils.AssetLibrary;
import sys.io.File;

#if MOD_SUPPORT
import funkin.backend.utils.SysZip.SysZipEntry;
Expand All @@ -15,37 +18,65 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
public var basePath:String;
public var modName:String;
public var libName:String;
public var useImageCache:Bool = false;
// public var useImageCache:Bool = false;
public var prefix = 'assets/';

public var zip:SysZip;
public var assets:Map<String, SysZipEntry> = [];
public var lowerCaseAssets:Map<String, SysZipEntry> = [];
public var nameMap:Map<String, String> = [];

public function new(basePath:String, libName:String, ?modName:String) {
public function new(basePath:String, libName:String, ?modName:String, ?preloadVideos:Bool = true) {
CoolUtil.debugTimeStamp();
this.libName = libName;

this.basePath = basePath;

this.modName = (modName == null) ? libName : modName;

zip = SysZip.openFromFile(basePath);
zip.read();
for(entry in zip.entries) {
lowerCaseAssets[entry.fileName.toLowerCase()] = assets[entry.fileName.toLowerCase()] = assets[entry.fileName] = entry;
nameMap.set(entry.fileName.toLowerCase(), entry.fileName);
var name = entry.fileName.toLowerCase();
lowerCaseAssets[name] = assets[name] = assets[entry.fileName] = entry;
nameMap.set(name, entry.fileName);
}

super();
isCompressed = true;
precacheVideos();
CoolUtil.debugTimeStamp("ZipFolderLibrary");
}

public function precacheVideos() {
videoCacheRemap = [];
for (entry in zip.entries) {
var name = entry.fileName.toLowerCase();
if (_videoExtensions.contains(Path.extension(name))) getPath(prefix+name);
}
var count = 0;
for (_ in videoCacheRemap.keys()) count++;
trace('Precached $count video${count == 1 ? "" : "s"}');
}

// Now we have supports for videos in ZIP!!
public var _videoExtensions:Array<String> = [Flags.VIDEO_EXT];
public var videoCacheRemap:Map<String, String> = [];
public function getVideoRemap(originalPath:String):String {
if (!_videoExtensions.contains(Path.extension(_parsedAsset))) return originalPath;
if (videoCacheRemap.exists(originalPath)) return videoCacheRemap.get(originalPath);

// We adding the length of the string to counteract folder in folder naming duplicates.
var newPath = './.temp/${assets[_parsedAsset].fileName.length}-zipvideo-${_parsedAsset.split("/").pop()}';
File.saveBytes(newPath, unzip(assets[_parsedAsset]));
videoCacheRemap.set(originalPath, newPath);
return newPath;
}

function toString():String {
return '(ZipFolderLibrary: $libName/$modName)';
return '(ZipFolderLibrary: $libName/$modName | ${zip.entries.length} entries | Detected Video Extensions: ${_videoExtensions.join(", ")})';
}

public var _parsedAsset:String;

public override function getAudioBuffer(id:String):AudioBuffer {
__parseAsset(id);
return AudioBuffer.fromBytes(unzip(assets[_parsedAsset]));
Expand All @@ -68,15 +99,12 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
return getAssetPath();
}



public inline function unzip(f:SysZipEntry)
return f == null ? null : zip.unzipEntry(f);
public inline function unzip(f:SysZipEntry) return (f == null) ? null : zip.unzipEntry(f);

public function __parseAsset(asset:String):Bool {
if (!asset.startsWith(prefix)) return false;
_parsedAsset = asset.substr(prefix.length);
if(ModsFolder.useLibFile) {
if (ModsFolder.useLibFile) {
var file = new haxe.io.Path(_parsedAsset);
if(file.file.startsWith("LIB_")) {
var library = file.file.substr(4);
Expand All @@ -87,8 +115,7 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
}

_parsedAsset = _parsedAsset.toLowerCase();
if(nameMap.exists(_parsedAsset))
_parsedAsset = nameMap.get(_parsedAsset);
if (nameMap.exists(_parsedAsset)) _parsedAsset = nameMap.get(_parsedAsset);
return true;
}

Expand All @@ -104,8 +131,7 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
}

private function getAssetPath() {
trace('[ZIP]$basePath/$_parsedAsset');
return '[ZIP]$basePath/$_parsedAsset';
return getVideoRemap('$basePath/$_parsedAsset');
}

// TODO: rewrite this to 1 function, like ModsFolderLibrary
Expand Down
3 changes: 3 additions & 0 deletions source/funkin/backend/system/Flags.hx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ class Flags {
// -- Codename's Addon Config --
@:bypass public static var addonFlags:Map<String, Dynamic> = [];

// -- Codename's ZipFolderLibrary Config --
public static var ALLOWED_ZIP_EXTENSIONS:Array<String> = ["zip"];

// -- Codename's Mod Config --
public static var MOD_NAME:String = "";
public static var MOD_DESCRIPTION:String = "";
Expand Down
1 change: 1 addition & 0 deletions source/funkin/backend/system/Main.hx
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ class Main extends Sprite
if (FileSystem.exists("mods/autoload.txt"))
modToLoad = File.getContent("mods/autoload.txt").trim();

trace('modToLoad: ${modToLoad}');
ModsFolder.switchMod(modToLoad.getDefault(Options.lastLoadedMod));
#end

Expand Down
45 changes: 31 additions & 14 deletions source/funkin/backend/system/MainState.hx
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,29 @@ class MainState extends FlxState {
var _highPriorityAddons:Array<AddonInfo> = [];
var _noPriorityAddons:Array<AddonInfo> = [];

var quick_modsPath = ModsFolder.modsPath + ModsFolder.currentModFolder;

var isCneMod = false;
for (ext in Flags.ALLOWED_ZIP_EXTENSIONS) {
if (!FileSystem.exists(quick_modsPath+"/cnemod."+ext)) continue;
isCneMod = true;
break;
}

// handing if the loading mod, before it's properly loaded, is a compressed mod
// we just need to use `Paths.assetsTree.hasCompressedLibrary` to complete valid checks for actual loaded compressed mods
var isZipMod = false;
for (ext in Flags.ALLOWED_ZIP_EXTENSIONS) {
if (!FileSystem.exists(quick_modsPath+"."+ext)) continue;
isZipMod = true;
break;
}

// Now here is a conundrum, if it's a compressed mod you can't really uncompress it yet, since it's not loaded as a library, so we need to skip it.
var addonPaths = [
ModsFolder.addonsPath,
(
ModsFolder.currentModFolder != null ?
ModsFolder.modsPath + ModsFolder.currentModFolder + "/addons/" :
null
( (ModsFolder.currentModFolder != null && !isZipMod) ?
quick_modsPath + "/addons/" : null
)
];

Expand All @@ -72,12 +89,8 @@ class MainState extends FlxState {

for (addon in FileSystem.readDirectory(path)) {
if (!FileSystem.isDirectory(path + addon)) {
switch(Path.extension(addon).toLowerCase()) {
case 'zip':
addon = Path.withoutExtension(addon);
default:
continue;
}
if (Flags.ALLOWED_ZIP_EXTENSIONS.contains(Path.extension(addon).toLowerCase())) addon = Path.withoutExtension(addon);
else continue;
}

var data:AddonInfo = {
Expand All @@ -100,9 +113,13 @@ class MainState extends FlxState {
#if MOD_SUPPORT
for (addon in _lowPriorityAddons)
loadLib(addon.path, ltrim(addon.name, "[LOW]"));

if (ModsFolder.currentModFolder != null)
loadLib(ModsFolder.modsPath + ModsFolder.currentModFolder, ModsFolder.currentModFolder);

if (ModsFolder.currentModFolder != null) {
if (isCneMod)
loadLib(quick_modsPath + "/cnemod", ModsFolder.currentModFolder);
else
loadLib(quick_modsPath, ModsFolder.currentModFolder);
}

for (addon in _noPriorityAddons)
loadLib(addon.path, addon.name);
Expand Down Expand Up @@ -136,7 +153,7 @@ class MainState extends FlxState {

var startState:Class<FlxState> = Flags.DISABLE_WARNING_SCREEN ? TitleState : funkin.menus.WarningState;

if (Options.devMode && Options.allowConfigWarning) {
if (Options.devMode && Options.allowConfigWarning && !Paths.assetsTree.hasCompressedLibrary) { // because it's a zip file, you can't edit a zip file without decompiling it
var lib:ModsFolderLibrary;
for (e in Paths.assetsTree.libraries) if ((lib = cast AssetsLibraryList.getCleanLibrary(e)) is ModsFolderLibrary
&& lib.modName == ModsFolder.currentModFolder)
Expand Down
1 change: 1 addition & 0 deletions source/funkin/backend/system/macros/Macros.hx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class Macros {
final fields:Array<Field> = Context.getBuildFields(), pos:Position = Context.currentPos();

fields.push({name: 'tag', access: [APublic], pos: pos, kind: FVar(macro :funkin.backend.assets.AssetSource)});
fields.push({name: 'isCompressed', access: [APublic], pos: pos, kind: FVar(macro :Bool, macro false)});

return fields;
}
Expand Down
15 changes: 15 additions & 0 deletions source/funkin/backend/utils/CoolUtil.hx
Original file line number Diff line number Diff line change
Expand Up @@ -1435,6 +1435,21 @@ final class CoolUtil

return toProperty.setValue(fromProperty.getValue());
}

private static var lastTimeStamp:Float = -1;
public static function debugTimeStamp(?customText:String = "Quick Debug") {
if (lastTimeStamp > 0) {
var endTimeStamp = haxe.Timer.stamp();
Logs.traceColored([
Logs.logText("[Haxe Time Stamp] ", YELLOW),
Logs.logText(customText),
Logs.logText(' | Time stamp took ${(endTimeStamp - lastTimeStamp)} milliseconds.'),
], INFO);
lastTimeStamp = -1;
return;
}
lastTimeStamp = haxe.Timer.stamp();
}
}

class PropertyInfo {
Expand Down
Loading