diff --git a/Project.xml b/Project.xml
index 143b6b18..1c383c35 100644
--- a/Project.xml
+++ b/Project.xml
@@ -96,7 +96,7 @@
-
+
@@ -151,7 +151,7 @@
-
+
@@ -164,6 +164,7 @@
+
diff --git a/buildnumber.txt b/buildnumber.txt
index ae6f1163..f4a8e832 100644
--- a/buildnumber.txt
+++ b/buildnumber.txt
@@ -1 +1 @@
-1507
\ No newline at end of file
+1513
\ No newline at end of file
diff --git a/source/funkin/menus/TitleState.hx b/source/funkin/menus/TitleState.hx
index 58b685b5..b27f5d02 100644
--- a/source/funkin/menus/TitleState.hx
+++ b/source/funkin/menus/TitleState.hx
@@ -175,12 +175,16 @@ class TitleState extends MusicBeatState
new FlxTimer().start(2, function(tmr:FlxTimer)
{
+ #if UPDATE_CHECKING
var report = funkin.updating.UpdateUtil.checkForUpdates();
if (report.newUpdate) {
FlxG.switchState(new funkin.updating.UpdateScreen(report));
} else {
FlxG.switchState(new MainMenuState());
}
+ #else
+ FlxG.switchState(new MainMenuState());
+ #end
});
}
diff --git a/source/funkin/system/AssetsLibraryList.hx b/source/funkin/system/AssetsLibraryList.hx
index 66dec5ae..008ee91a 100644
--- a/source/funkin/system/AssetsLibraryList.hx
+++ b/source/funkin/system/AssetsLibraryList.hx
@@ -45,11 +45,13 @@ class AssetsLibraryList extends AssetLibrary {
}
// TODO: do base folder scanning
+ #if MODS_FOLDER
if (l is ModsAssetLibrary) {
var lib = cast(l, ModsAssetLibrary);
for(e in lib.getFiles(folder))
content.push(e);
}
+ #end
}
return content;
}
@@ -95,6 +97,21 @@ class AssetsLibraryList extends AssetLibrary {
}
}
+ public override function isLocal(id:String, type:String) {
+ return true;
+ // for(l in libraries) {
+ // var lib:AssetLibrary = l;
+ // if (lib is openfl.utils.AssetLibrary) {
+ // var openflLib = cast(lib, openfl.utils.AssetLibrary);
+ // @:privateAccess
+ // if (openflLib.__proxy != null) lib = openflLib.__proxy;
+ // }
+ // if (lib.exists(id, type) && lib.isLocal(id, type))
+ // return true;
+ // }
+ // return false;
+ }
+
public function new(?base:AssetLibrary) {
super();
if (base == null)
diff --git a/source/funkin/system/Main.hx b/source/funkin/system/Main.hx
index a271746e..70139dab 100644
--- a/source/funkin/system/Main.hx
+++ b/source/funkin/system/Main.hx
@@ -151,8 +151,6 @@ class Main extends Sprite
#if USE_SOURCE_ASSETS
trace("Used lime test windows. Switching into source assets.");
Paths.assetsTree.addLibrary(ModsFolder.loadLibraryFromFolder('assets', './../../../../assets/', true));
- #else
- Assets.registerLibrary('assets', Paths.assetsTree.base);
#end
var buildNum:Int = Std.parseInt(File.getContent("./../../../../buildnumber.txt"));
@@ -161,12 +159,8 @@ class Main extends Sprite
} else {
#if USE_ADAPTED_ASSETS
Paths.assetsTree.addLibrary(ModsFolder.loadLibraryFromFolder('assets', './assets/', true));
- #else
- Assets.registerLibrary('assets', Paths.assetsTree.base);
#end
}
- #else
- Assets.registerLibrary('assets', Paths.assetsTree.base);
#end
diff --git a/source/funkin/system/MusicBeatState.hx b/source/funkin/system/MusicBeatState.hx
index aa271226..aace2587 100644
--- a/source/funkin/system/MusicBeatState.hx
+++ b/source/funkin/system/MusicBeatState.hx
@@ -1,21 +1,15 @@
package funkin.system;
-import sys.thread.Thread;
-import flixel.FlxBasic;
import funkin.scripting.DummyScript;
import flixel.FlxState;
import flixel.FlxSubState;
import funkin.scripting.events.*;
import funkin.scripting.Script;
import funkin.interfaces.IBeatReceiver;
-import funkin.system.Conductor.BPMChangeEvent;
import funkin.system.Conductor;
import flixel.FlxG;
-import flixel.addons.transition.FlxTransitionableState;
import flixel.addons.ui.FlxUIState;
-import flixel.math.FlxRect;
import flixel.math.FlxMath;
-import flixel.util.FlxTimer;
import funkin.options.PlayerSettings;
class MusicBeatState extends FlxUIState implements IBeatReceiver
diff --git a/source/funkin/system/ZipUtils.hx b/source/funkin/system/ZipUtils.hx
new file mode 100644
index 00000000..adebb0ef
--- /dev/null
+++ b/source/funkin/system/ZipUtils.hx
@@ -0,0 +1,266 @@
+package funkin.system;
+
+#if (!macro && sys)
+import openfl.display.BitmapData;
+#end
+
+import haxe.Exception;
+import haxe.Json;
+import haxe.crypto.Crc32;
+import haxe.zip.Writer;
+import haxe.zip.Tools;
+import haxe.zip.Entry;
+import haxe.zip.Uncompress;
+import haxe.zip.Reader;
+import haxe.zip.Compress;
+import sys.FileSystem;
+import sys.io.File;
+import sys.thread.Thread;
+
+using StringTools;
+
+// import ZipUtils; ZipUtils.uncompressZip(ZipUtils.openZip("E:\\Desktop\\test\\termination lua.ycemod"), "E:\\Desktop\\test\\uncompressed\\");
+// import ZipUtils; var e = ZipUtils.createZipFile("gjnsdghs.ycemod"); ZipUtils.writeFolderToZip(e, "./mods/Friday Night Funkin'/", "Friday Night Funkin'/"); e.flush(); e.close();
+
+class ZipUtils {
+ public static var bannedNames:Array = [".git", ".gitignore", ".github", ".vscode", ".gitattributes", "readme.txt"];
+
+ /**
+ * [Description] Uncompresses `zip` into the `destFolder` folder
+ * @param zip
+ * @param destFolder
+ */
+ public static function uncompressZip(zip:Reader, destFolder:String, ?prefix:String, ?prog:ZipProgress):ZipProgress {
+ // we never know
+ FileSystem.createDirectory(destFolder);
+
+ var fields = zip.read();
+
+ try {
+ if (prefix != null) {
+ var f = fields;
+ fields = new List();
+ for(field in f) {
+ if (field.fileName.startsWith(prefix)) {
+ fields.push(field);
+ }
+ }
+ }
+
+ if (prog == null)
+ prog = new ZipProgress();
+ prog.fileCount = fields.length;
+ for(k=>field in fields) {
+ prog.curFile = k;
+ var isFolder = field.fileName.endsWith("/") && field.fileSize == 0;
+ if (isFolder) {
+ FileSystem.createDirectory('${destFolder}/${field.fileName}');
+ } else {
+ var split = [for(e in field.fileName.split("/")) e.trim()];
+ split.pop();
+ FileSystem.createDirectory('${destFolder}/${split.join("/")}');
+
+ var data = unzip(field);
+ File.saveBytes('${destFolder}/${field.fileName}', data);
+ }
+ }
+ prog.curFile = fields.length;
+ prog.done = true;
+ } catch(e) {
+ prog.done = true;
+ prog.error = e;
+ }
+ return prog;
+ }
+
+ #if (!macro && sys)
+ public static function uncompressZipAsync(zip:Reader, destFolder:String, ?prog:ZipProgress, ?prefix:String):ZipProgress {
+ if (prog == null)
+ prog = new ZipProgress();
+ Thread.create(function() {
+ uncompressZip(zip, destFolder, prefix, prog);
+ });
+ return prog;
+ }
+ #end
+
+ /**
+ * [Description] Returns a `zip.Reader` instance from path.
+ * @param zipPath
+ * @return Reader
+ */
+ public static function openZip(zipPath:String):Reader {
+ return new ZipReader(File.read(zipPath));
+ }
+
+ /**
+ * [Description] Copy of haxe's Zip unzip function cause lime replaced it.
+ * @param f Zip entry
+ */
+ public static function unzip(f:Entry) {
+ if (!f.compressed)
+ return f.data;
+ var c = new haxe.zip.Uncompress(-15);
+ var s = haxe.io.Bytes.alloc(f.fileSize);
+ var r = c.execute(f.data, 0, s, 0);
+ c.close();
+ if (!r.done || r.read != f.data.length || r.write != f.fileSize)
+ throw "Invalid compressed data for " + f.fileName;
+ f.compressed = false;
+ f.dataSize = f.fileSize;
+ f.data = s;
+ return f.data;
+ }
+
+ /**
+ * [Description] Creates a ZIP file at the specified location and returns the Writer.
+ * @param path
+ * @return Writer
+ */
+ public static function createZipFile(path:String):ZipWriter {
+ var output = File.write(path);
+ return new ZipWriter(output);
+ }
+
+ /**
+ [Description] Writes the entirety of a folder to a zip file.
+ @param zip ZIP file to write to
+ @param path Folder path
+ @param prefix (Additional) allows you to set a prefix in the zip itself.
+ **/
+ public static function writeFolderToZip(zip:ZipWriter, path:String, ?prefix:String, ?prog:ZipProgress, ?whitelist:Array):ZipProgress {
+ if (prefix == null) prefix = "";
+ if (whitelist == null) whitelist = [];
+ if (prog == null) prog = new ZipProgress();
+
+ try {
+ var curPath:Array = ['$path'];
+ var destPath:Array = [];
+ if (prefix != "") {
+ prefix = prefix.replace("\\", "/");
+ while(prefix.charAt(0) == "/") prefix = prefix.substr(1);
+ while(prefix.charAt(prefix.length-1) == "/") prefix = prefix.substr(0, prefix.length-1);
+ destPath.push(prefix);
+ }
+
+ var files:Array = [];
+
+ var doFolder:Void->Void = null;
+ (doFolder = function() {
+ var path = curPath.join("/");
+ var zipPath = destPath.join("/");
+ for(e in FileSystem.readDirectory(path)) {
+ if (bannedNames.contains(e.toLowerCase()) && !whitelist.contains(e.toLowerCase())) continue;
+ if (FileSystem.isDirectory('$path/$e')) {
+ // is directory, so loop into that function again
+ for(p in [curPath, destPath]) p.push(e);
+ doFolder();
+ for(p in [curPath, destPath]) p.pop();
+ } else {
+ // is file, put it in the list
+ var zipPath = '$zipPath/$e';
+ while(zipPath.charAt(0) == "/") zipPath = zipPath.substr(1);
+ files.push(new StrNameLabel('$path/$e', zipPath));
+ }
+ }
+ })();
+
+ prog.fileCount = files.length;
+ for(k=>file in files) {
+ prog.curFile = k;
+
+ var fileContent = File.getBytes(file.name);
+ var fileInfo = FileSystem.stat(file.name);
+ var entry:Entry = {
+ fileName: file.label,
+ fileSize: fileInfo.size,
+ fileTime: Date.now(),
+ dataSize: 0,
+ data: fileContent,
+ crc32: Crc32.make(fileContent), // TODO???
+ compressed: false
+ };
+ Tools.compress(entry, 1);
+ zip.writeFile(entry);
+ }
+ zip.writeCDR();
+ } catch(e) {
+ prog.error = e;
+ }
+ prog.done = true;
+ return prog;
+ }
+
+ public static function writeFolderToZipAsync(zip:ZipWriter, path:String, ?prefix:String):ZipProgress {
+ var zipProg = new ZipProgress();
+ Thread.create(function() {
+ writeFolderToZip(zip, path, prefix, zipProg);
+ });
+ return zipProg;
+ }
+
+ /**
+ * [Description] Converts an `Array` to a `List`.
+ * @param array
+ * @return List
+ */
+ public static function arrayToList(array:Array):List {
+ var list = new List();
+ for(e in array) list.push(e);
+ return list;
+ }
+}
+
+class ZipProgress {
+ public var error:Exception = null;
+
+ public var curFile:Int = 0;
+ public var fileCount:Int = 0;
+ public var done:Bool = false;
+ public var percentage(get, null):Float;
+
+ private function get_percentage() {
+ return fileCount <= 0 ? 0 : curFile / fileCount;
+ }
+
+ public function new() {}
+}
+
+class ZipReader extends Reader {
+ public var files:List;
+
+ public override function read() {
+ if (files != null) return files;
+ try {
+ var files = super.read();
+ return this.files = files;
+ } catch(e) {
+ }
+ return new List();
+ }
+}
+
+class ZipWriter extends Writer {
+ public function flush() {
+ o.flush();
+ }
+
+ public function writeFile(entry:Entry) {
+ writeEntryHeader(entry);
+ o.writeFullBytes(entry.data, 0, entry.data.length);
+ }
+
+ public function close() {
+ o.close();
+ }
+}
+
+class StrNameLabel {
+ public var name:String;
+ public var label:String;
+
+ public function new(name:String, label:String) {
+ this.name = name;
+ this.label = label;
+ }
+}
\ No newline at end of file
diff --git a/source/funkin/updating/AsyncUpdater.hx b/source/funkin/updating/AsyncUpdater.hx
index 7d45e04f..68fb73fb 100644
--- a/source/funkin/updating/AsyncUpdater.hx
+++ b/source/funkin/updating/AsyncUpdater.hx
@@ -1,5 +1,15 @@
package funkin.updating;
+import haxe.zip.Reader;
+import funkin.system.ZipUtils;
+import haxe.io.Path;
+import openfl.utils.ByteArray;
+import sys.io.File;
+import sys.io.FileOutput;
+import openfl.events.Event;
+import openfl.events.ProgressEvent;
+import openfl.net.URLRequest;
+import openfl.net.URLLoader;
import sys.FileSystem;
import funkin.github.GitHubRelease;
@@ -15,13 +25,98 @@ class AsyncUpdater {
}
#end
+
+ #if windows
+ public var executableGitHubName:String = "update-windows.exe";
+ public var executableName:String = "CodenameEngine.exe";
+ #end
+ #if linux
+ public var executableGitHubName:String = "update-linux";
+ public var executableName:String = "CodenameEngine";
+ #end
+
public var releases:Array;
public var progress:UpdaterProgress = new UpdaterProgress();
public var path:String;
+ public var downloadStream:URLLoader;
public function installUpdates() {
prepareInstallationEnvironment();
+ downloadFiles();
+ }
+ public function installFiles(files:Array) {
+ progress.step = INSTALLING;
+ for(e in files) {
+ var path = '$path$e';
+ trace('extracting file ${path}');
+ var reader = ZipUtils.openZip(path);
+
+ progress.curZipProgress = new ZipProgress();
+ ZipUtils.uncompressZip(reader, './', null, progress.curZipProgress);
+ }
+ }
+
+ public function downloadFiles() {
+ var files:Array = [];
+ var fileNames:Array = [];
+ var exePath:String = "";
+ for(r in releases) {
+ for(e in r.assets) {
+ if (e.name.toLowerCase() == "update-assets.zip") {
+ files.push(e.browser_download_url);
+ fileNames.push('${Path.withoutExtension(e.name)}-${r.tag_name}.${Path.extension(e.name)}');
+ } else if (e.name.toLowerCase() == executableGitHubName) {
+ exePath = e.browser_download_url;
+ }
+ }
+ }
+ progress.curFile = 0;
+ progress.files = files.length;
+ progress.step = DOWNLOADING_ASSETS;
+ trace('starting assets download');
+ doFile([for(e in files) e], [for(e in fileNames) e], function() {
+ progress.curFile = 0;
+ progress.files = 1;
+ progress.step = DOWNLOADING_EXECUTABLE;
+ trace('starting exe download');
+ // doFile([exePath], [executableName], function() {
+ trace('done, starting installation');
+ installFiles(fileNames);
+ // });
+ });
+ }
+
+ public function doFile(files:Array, fileNames:Array, onFinish:Void->Void) {
+ var f = files.shift();
+ if (f == null) {
+ onFinish();
+ return;
+ }
+ var fn = fileNames.shift();
+ trace('downloading $f ($fn)');
+ progress.curFile++;
+ downloadStream = new URLLoader();
+ downloadStream.dataFormat = BINARY;
+
+ downloadStream.addEventListener(ProgressEvent.PROGRESS, function(e) {
+ progress.bytesLoaded = e.bytesLoaded;
+ progress.bytesTotal = e.bytesTotal;
+ trace('${progress.bytesLoaded} / ${progress.bytesTotal}');
+ });
+ downloadStream.addEventListener(Event.COMPLETE, function(e) {
+ var fileOutput:FileOutput = File.write('$path$fn', true);
+
+ var data:ByteArray = new ByteArray();
+ downloadStream.data.readBytes(data, 0, downloadStream.data.length - downloadStream.data.position);
+ fileOutput.writeBytes(data, 0, data.length);
+ fileOutput.flush();
+
+ fileOutput.close();
+ doFile(files, fileNames, onFinish);
+ });
+
+ downloadStream.load(new URLRequest(f));
}
public function prepareInstallationEnvironment() {
@@ -39,6 +134,11 @@ class AsyncUpdater {
class UpdaterProgress {
public var step:UpdaterStep = PREPARING;
+ public var curFile:Int = 0;
+ public var files:Int = 0;
+ public var bytesLoaded:Float = 0;
+ public var bytesTotal:Float = 0;
+ public var curZipProgress:ZipProgress = null;
public function new() {}
}