Skip to content

Commit 4247ec5

Browse files
committed
Merge branch 'main' into internal-merge
# Conflicts: # building/libs.xml # source/openfl/utils/Assets.hx
2 parents 491e5df + 3266e22 commit 4247ec5

File tree

12 files changed

+263
-202
lines changed

12 files changed

+263
-202
lines changed

source/funkin/backend/assets/AssetsLibraryList.hx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,19 @@ import lime.utils.AssetLibrary;
88
import haxe.ds.Map;
99

1010
class AssetsLibraryList extends AssetLibrary {
11+
1112
public var libraries:Array<AssetLibrary> = [];
13+
public var cleanLibraries(get, never):Array<AssetLibrary>;
14+
function get_cleanLibraries():Array<AssetLibrary> {
15+
return [for (l in libraries) getCleanLibrary(l)];
16+
}
17+
18+
// is true if any library in `libraries` contains some kind of compressed library.
19+
public var hasCompressedLibrary(get, never):Bool;
20+
function get_hasCompressedLibrary():Bool {
21+
for (l in libraries) if (getCleanLibrary(l).isCompressed) return true;
22+
return false;
23+
}
1224

1325
@:allow(funkin.backend.system.Main)
1426
@:allow(funkin.backend.system.MainState)

source/funkin/backend/assets/ModsFolder.hx

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,11 @@ class ModsFolder {
8383
*/
8484
public static function loadModLib(path:String, force:Bool = false, ?modName:String) {
8585
#if MOD_SUPPORT
86-
if (FileSystem.exists('$path.zip'))
87-
return loadLibraryFromZip('$path'.toLowerCase(), '$path.zip', force, modName);
88-
else
89-
return loadLibraryFromFolder('$path'.toLowerCase(), '$path', force, modName);
86+
for (ext in Flags.ALLOWED_ZIP_EXTENSIONS) {
87+
if (!FileSystem.exists('$path.$ext')) continue;
88+
return loadLibraryFromZip('$path'.toLowerCase(), '$path.$ext', force, modName);
89+
}
90+
return loadLibraryFromFolder('$path'.toLowerCase(), '$path', force, modName);
9091

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

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

106-
if (modsList == null || modsList.length <= 0)
107-
return mods;
105+
if (modsList == null || modsList.length <= 0) return mods;
108106

109107
for (modFolder in modsList) {
110-
if (FileSystem.isDirectory(modsPath + modFolder)) {
111-
mods.push(modFolder);
112-
} else {
113-
var ext = Path.extension(modFolder).toLowerCase();
114-
switch(ext) {
115-
case 'zip':
116-
// is a zip mod!!
117-
mods.push(Path.withoutExtension(modFolder));
118-
}
119-
}
108+
if (FileSystem.isDirectory(modsPath + modFolder)) mods.push(modFolder);
109+
else if (Flags.ALLOWED_ZIP_EXTENSIONS.contains(Path.extension(modFolder))) mods.push(Path.withoutExtension(modFolder));
120110
}
121111
#end
122112
return mods;
@@ -128,7 +118,9 @@ class ModsFolder {
128118
#if TRANSLATIONS_SUPPORT
129119
if(skipTranslated && (l is TranslatedAssetLibrary)) continue;
130120
#end
131-
if (l is ScriptedAssetLibrary || l is IModsAssetLibrary) libs.push(cast(l, IModsAssetLibrary));
121+
// No need to check for it being a `ScriptedAssetLibrary`, if `ScriptedAssetLibrary` extends ModsFolderLibrary, which implements `IModsAssetLibrary`
122+
// If you have to revert this change then uhhhhh wasn't me, trust 🙏
123+
if (/*l is ScriptedAssetLibrary ||*/ l is IModsAssetLibrary) libs.push(cast(l, IModsAssetLibrary));
132124
}
133125
return libs;
134126
}

source/funkin/backend/assets/ZipFolderLibrary.hx

Lines changed: 55 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package funkin.backend.assets;
22

3+
import funkin.backend.system.Flags;
4+
35
import haxe.io.Path;
46
import lime.graphics.Image;
57
import lime.media.AudioBuffer;
68
import lime.text.Font;
79
import lime.utils.Bytes;
810
import openfl.utils.AssetLibrary;
11+
import sys.io.File;
912

1013
#if MOD_SUPPORT
1114
import funkin.backend.utils.SysZip.SysZipEntry;
@@ -15,40 +18,76 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
1518
public var basePath:String;
1619
public var modName:String;
1720
public var libName:String;
18-
public var useImageCache:Bool = false;
1921
public var prefix = 'assets/';
20-
22+
2123
public var zip:SysZip;
2224
public var assets:Map<String, SysZipEntry> = [];
2325
public var lowerCaseAssets:Map<String, SysZipEntry> = [];
2426
public var nameMap:Map<String, String> = [];
2527

26-
public function new(basePath:String, libName:String, ?modName:String) {
28+
public var PRELOAD_VIDEOS:Bool = true;
29+
30+
public function new(basePath:String, libName:String, ?modName:String, ?preloadVideos:Bool = true) {
2731
this.libName = libName;
2832

2933
this.basePath = basePath;
3034

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

3337
zip = SysZip.openFromFile(basePath);
34-
zip.read();
3538
for(entry in zip.entries) {
36-
if (entry.fileName.length < 0 || entry.fileName.endsWith("/"))
37-
continue;
39+
if (entry.fileName.length < 0 || entry.fileName.endsWith("/")) continue;
3840

39-
lowerCaseAssets[entry.fileName.toLowerCase()] = assets[entry.fileName.toLowerCase()] = assets[entry.fileName] = entry;
40-
nameMap.set(entry.fileName.toLowerCase(), entry.fileName);
41+
var name:String = entry.fileName.toLowerCase(); // calling .toLowerCase a million times is never the solution
42+
lowerCaseAssets[name] = assets[name] = assets[entry.fileName] = entry;
43+
nameMap.set(name, entry.fileName);
4144
}
4245

4346
super();
47+
48+
isCompressed = true;
49+
50+
// don't override default value of true if the file exists.
51+
// by default `PRELOAD_VIDEOS` is true so you will never need to add this file, but in the case of it being false this is a backup method.
52+
PRELOAD_VIDEOS = (!PRELOAD_VIDEOS) ? exists("assets/data/PRECACHE_VIDEOS", "TEXT") : PRELOAD_VIDEOS;
53+
54+
// if (PRELOAD_VIDEOS) precacheVideos(); // we do this in `MainState` now to handle for `Flags.VIDEO_EXT` :)
55+
}
56+
57+
public function precacheVideos() {
58+
_videoExtensions = [Flags.VIDEO_EXT];
59+
60+
videoCacheRemap = [];
61+
for (entry in zip.entries) {
62+
var name = entry.fileName.toLowerCase();
63+
if (_videoExtensions.contains(Path.extension(name))) getPath(prefix+name);
64+
}
65+
66+
var count:Int = 0;
67+
for (_ in videoCacheRemap.keys()) count++;
68+
if (count <= 0) return;
69+
trace('Precached $count video${(count == 1) ? "" : "s"}');
70+
}
71+
72+
// Now we have supports for videos in ZIP!!
73+
public var _videoExtensions:Array<String> = [Flags.VIDEO_EXT];
74+
public var videoCacheRemap:Map<String, String> = [];
75+
public function getVideoRemap(originalPath:String):String {
76+
if (!_videoExtensions.contains(Path.extension(_parsedAsset))) return originalPath;
77+
if (videoCacheRemap.exists(originalPath)) return videoCacheRemap.get(originalPath);
78+
79+
// We adding the length of the string to counteract folder in folder naming duplicates.
80+
var newPath = './.temp/${_parsedAsset.length}-zipvideo-${_parsedAsset.split("/").pop()}';
81+
File.saveBytes(newPath, unzip(assets[_parsedAsset]));
82+
videoCacheRemap.set(originalPath, newPath);
83+
return newPath;
4484
}
4585

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

5090
public var _parsedAsset:String;
51-
5291
public override function getAudioBuffer(id:String):AudioBuffer {
5392
__parseAsset(id);
5493
return AudioBuffer.fromBytes(unzip(assets[_parsedAsset]));
@@ -71,15 +110,12 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
71110
return getAssetPath();
72111
}
73112

74-
75-
76-
public inline function unzip(f:SysZipEntry)
77-
return f == null ? null : zip.unzipEntry(f);
113+
public inline function unzip(f:SysZipEntry) return (f == null) ? null : zip.unzipEntry(f);
78114

79115
public function __parseAsset(asset:String):Bool {
80116
if (!asset.startsWith(prefix)) return false;
81117
_parsedAsset = asset.substr(prefix.length);
82-
if(ModsFolder.useLibFile) {
118+
if (ModsFolder.useLibFile) {
83119
var file = new haxe.io.Path(_parsedAsset);
84120
if(file.file.startsWith("LIB_")) {
85121
var library = file.file.substr(4);
@@ -90,8 +126,7 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
90126
}
91127

92128
_parsedAsset = _parsedAsset.toLowerCase();
93-
if(nameMap.exists(_parsedAsset))
94-
_parsedAsset = nameMap.get(_parsedAsset);
129+
if (nameMap.exists(_parsedAsset)) _parsedAsset = nameMap.get(_parsedAsset);
95130
return true;
96131
}
97132

@@ -106,9 +141,8 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
106141
return assets[_parsedAsset] != null;
107142
}
108143

109-
private function getAssetPath() {
110-
trace('[ZIP]$basePath/$_parsedAsset');
111-
return '[ZIP]$basePath/$_parsedAsset';
144+
private inline function getAssetPath() {
145+
return getVideoRemap('$basePath/$_parsedAsset');
112146
}
113147

114148
// TODO: rewrite this to 1 function, like ModsFolderLibrary
@@ -157,18 +191,6 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
157191
return content;
158192
}
159193

160-
public override function list(type:String):Array<String> {
161-
return[for(k=>e in nameMap) '$prefix$e'];
162-
}
163-
164-
// Backwards compat
165-
166-
@:noCompletion public var zipPath(get, set):String;
167-
@:noCompletion private inline function get_zipPath():String {
168-
return basePath;
169-
}
170-
@:noCompletion private inline function set_zipPath(value:String):String {
171-
return basePath = value;
172-
}
194+
public override function list(type:String):Array<String> { return [for(k=>e in nameMap) '$prefix$e']; }
173195
}
174196
#end

source/funkin/backend/scripting/GlobalScript.hx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,15 @@ class GlobalScript {
106106
public static function onModSwitch(newMod:String) {
107107
destroy();
108108
scripts = new ScriptPack("GlobalScript");
109-
for (i in funkin.backend.assets.ModsFolder.getLoadedMods()) {
110-
var path = Paths.script('data/global/LIB_$i');
109+
for (lib in funkin.backend.assets.ModsFolder.getLoadedModsLibs()) {
110+
var modName = lib.modName;
111+
var path = Paths.script('data/global/LIB_$modName');
111112
var script = Script.create(path);
112-
if (script is DummyScript)
113-
continue;
114-
script.remappedNames.set(script.fileName, '$i:${script.fileName}');
113+
if (script is DummyScript) continue;
114+
script.remappedNames.set(script.fileName, '$modName:${script.fileName}');
115+
// so you can get the current mod's library in GloablScript :)
116+
// you should not make this a static variable then all scripts will try to reference the 1 static variable, which will be overwritten :yoikes:
117+
script.set("MOD_LIBRARY", lib);
115118
scripts.add(script);
116119
script.load();
117120
}

source/funkin/backend/system/Flags.hx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@ class Flags {
1919

2020
// -- Codename's Addon Config --
2121
@:bypass public static var addonFlags:Map<String, Dynamic> = [];
22-
2322
public static var CURRENT_API_VERSION:Int = 2;
2423

24+
// -- Codename's ZipFolderLibrary Config --
25+
public static var ALLOWED_ZIP_EXTENSIONS:Array<String> = ["zip"];
26+
2527
// -- Codename's Mod Config --
2628
public static var MOD_NAME:String = "";
2729
public static var MOD_DESCRIPTION:String = "";

source/funkin/backend/system/MainState.hx

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ import flixel.FlxState;
77
import funkin.backend.assets.AssetsLibraryList;
88
import funkin.backend.assets.ModsFolder;
99
import funkin.backend.assets.ModsFolderLibrary;
10+
import funkin.backend.assets.ZipFolderLibrary;
1011
import funkin.backend.chart.EventsData;
1112
import funkin.backend.system.framerate.Framerate;
1213
import funkin.editors.ModConfigWarning;
1314
import funkin.menus.TitleState;
1415
import haxe.io.Path;
1516

17+
1618
@dox(hide)
1719
typedef AddonInfo = {
1820
var name:String;
@@ -58,12 +60,32 @@ class MainState extends FlxState {
5860
var _highPriorityAddons:Array<AddonInfo> = [];
5961
var _noPriorityAddons:Array<AddonInfo> = [];
6062

63+
var quick_modsPath = ModsFolder.modsPath + ModsFolder.currentModFolder;
64+
65+
// handing if the loading mod (before it's properly loaded) is a compressed mod
66+
// we just need to use `Paths.assetsTree.hasCompressedLibrary` to complete valid checks for actual loaded compressed mods
67+
var isZipMod = false;
68+
69+
// If we know it's a compressed mod, then we can check if it's using the `cnemod` folder path.
70+
// All it is really is a folder with the mod's name, then a compressed file called "cnemod.[zip|7z|rar|etc]"
71+
var isCneMod = false;
72+
73+
// We are doing it like this because think about it: it's 1 for loop lol
74+
// We just need to know if any of these values is true, so if only one is true and we are not close to being done in the loop, that's fine.
75+
//
76+
for (ext in Flags.ALLOWED_ZIP_EXTENSIONS) {
77+
if (FileSystem.exists(quick_modsPath+"."+ext)) isZipMod = true;
78+
if (FileSystem.exists(quick_modsPath+"/cnemod."+ext)) isCneMod = true;
79+
if (isZipMod && isCneMod) break;
80+
}
81+
82+
// We get the addons folder from relative space (`./`) and then our mod's addons.
6183
var addonPaths = [
6284
ModsFolder.addonsPath,
63-
(
64-
ModsFolder.currentModFolder != null ?
65-
ModsFolder.modsPath + ModsFolder.currentModFolder + "/addons/" :
66-
null
85+
// So to check the mod's addons folder, we need to decompress it. Which is impossible* in this stage of the loading library process.
86+
// TODO: Write a function when the library is loaded to decompress the contents and then load the libraries :)
87+
( (ModsFolder.currentModFolder != null && !isZipMod) ?
88+
quick_modsPath + "/addons/" : null
6789
)
6890
];
6991

@@ -73,12 +95,8 @@ class MainState extends FlxState {
7395

7496
for (addon in FileSystem.readDirectory(path)) {
7597
if (!FileSystem.isDirectory(path + addon)) {
76-
switch(Path.extension(addon).toLowerCase()) {
77-
case 'zip':
78-
addon = Path.withoutExtension(addon);
79-
default:
80-
continue;
81-
}
98+
if (Flags.ALLOWED_ZIP_EXTENSIONS.contains(Path.extension(addon))) addon = Path.withoutExtension(addon);
99+
else continue;
82100
}
83101

84102
var data:AddonInfo = {
@@ -101,9 +119,14 @@ class MainState extends FlxState {
101119
#if MOD_SUPPORT
102120
for (addon in _lowPriorityAddons)
103121
loadLib(addon.path, ltrim(addon.name, "[LOW]"));
104-
105-
if (ModsFolder.currentModFolder != null)
106-
loadLib(ModsFolder.modsPath + ModsFolder.currentModFolder, ModsFolder.currentModFolder);
122+
123+
if (ModsFolder.currentModFolder != null) {
124+
// isCneMod is a guarentee to be a zip mod because we just checked for it, so this will always load as a CompressedLibrary
125+
if (isCneMod)
126+
loadLib(quick_modsPath + "/cnemod", ModsFolder.currentModFolder);
127+
else
128+
loadLib(quick_modsPath, ModsFolder.currentModFolder);
129+
}
107130

108131
for (addon in _noPriorityAddons)
109132
loadLib(addon.path, addon.name);
@@ -135,9 +158,15 @@ class MainState extends FlxState {
135158
CoolUtil.safeAddAttributes('./.temp/', NativeAPI.FileAttribute.HIDDEN);
136159
#end
137160

161+
for (lib in ModsFolder.getLoadedModsLibs()) {
162+
if (!(lib is ZipFolderLibrary)) continue;
163+
if (cast(lib, ZipFolderLibrary).PRELOAD_VIDEOS) cast(lib, ZipFolderLibrary).precacheVideos();
164+
}
165+
138166
var startState:Class<FlxState> = Flags.DISABLE_WARNING_SCREEN ? TitleState : funkin.menus.WarningState;
139167

140-
if (Options.devMode && Options.allowConfigWarning) {
168+
// In this case if the mod we just loaded a compressed modpack, we can't edit or modify files without decompressing it.
169+
if (Options.devMode && Options.allowConfigWarning && !isZipMod) {
141170
var lib:ModsFolderLibrary;
142171
for (e in Paths.assetsTree.libraries) if ((lib = cast AssetsLibraryList.getCleanLibrary(e)) is ModsFolderLibrary
143172
&& lib.modName == ModsFolder.currentModFolder)

0 commit comments

Comments
 (0)