fixed zip mods ram usage & in general
This commit is contained in:
@@ -14,6 +14,7 @@ import lime.text.Font;
|
||||
import lime.utils.AssetType;
|
||||
import lime.utils.Bytes;
|
||||
import lime.utils.Assets as LimeAssets;
|
||||
import openfl.text.Font as OpenFLFont;
|
||||
|
||||
|
||||
#if MOD_SUPPORT
|
||||
@@ -21,108 +22,60 @@ import sys.FileStat;
|
||||
import sys.FileSystem;
|
||||
import sys.io.File;
|
||||
import haxe.zip.Reader;
|
||||
import haxe.zip.Entry;
|
||||
#end
|
||||
import funkin.utils.SysZip;
|
||||
import funkin.utils.SysZip.SysZipEntry;
|
||||
|
||||
class ZipFolderLibrary extends AssetLibrary implements ModsAssetLibrary {
|
||||
public var zipPath:String;
|
||||
public var libName:String;
|
||||
public var useImageCache:Bool = false;
|
||||
public var prefix = 'assets/';
|
||||
|
||||
#if MOD_SUPPORT
|
||||
public var zip:Reader;
|
||||
public var assets:Map<String, Entry> = [];
|
||||
#end
|
||||
|
||||
public var zip:SysZip;
|
||||
public var assets:Map<String, SysZipEntry> = [];
|
||||
|
||||
public function new(zipPath:String, libName:String) {
|
||||
this.zipPath = zipPath;
|
||||
this.libName = libName;
|
||||
this.prefix = 'assets/$libName/';
|
||||
|
||||
#if MOD_SUPPORT
|
||||
zip = new Reader(File.read(zipPath, true));
|
||||
var entries = zip.read();
|
||||
for(entry in entries) {
|
||||
zip = SysZip.openFromFile(zipPath);
|
||||
zip.read();
|
||||
for(entry in zip.entries)
|
||||
assets[entry.fileName.toLowerCase()] = entry;
|
||||
}
|
||||
#end
|
||||
|
||||
super();
|
||||
}
|
||||
|
||||
#if MOD_SUPPORT
|
||||
public var _parsedAsset:String;
|
||||
|
||||
|
||||
public override function getAudioBuffer(id:String):AudioBuffer {
|
||||
if (__isCacheValid(LimeAssets.cache.audio, id))
|
||||
return LimeAssets.cache.audio.get('$libName:$id');
|
||||
else {
|
||||
if (!exists(id, "SOUND")) {
|
||||
Log.error('ZipFolderLibrary: Audio Buffer at $id does not exist.');
|
||||
return null;
|
||||
}
|
||||
var e = AudioBuffer.fromBytes(unzip(assets[_parsedAsset]));
|
||||
return e;
|
||||
}
|
||||
__parseAsset(id);
|
||||
return AudioBuffer.fromBytes(unzip(assets[_parsedAsset]));
|
||||
}
|
||||
|
||||
public override function getBytes(id:String):Bytes {
|
||||
if (__isCacheValid(cachedBytes, id, true))
|
||||
return cachedBytes.get(id);
|
||||
else {
|
||||
if (!exists(id, "BINARY")) {
|
||||
Log.error('ZipFolderLibrary: Bytes at $id does not exist.');
|
||||
return null;
|
||||
}
|
||||
var e = Bytes.fromBytes(unzip(assets[_parsedAsset]));
|
||||
cachedBytes.set(id, e);
|
||||
return e;
|
||||
}
|
||||
__parseAsset(id);
|
||||
return Bytes.fromBytes(unzip(assets[_parsedAsset]));
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public override function getFont(id:String):Font {
|
||||
if (__isCacheValid(LimeAssets.cache.font, id))
|
||||
return LimeAssets.cache.font.get('$libName:$id');
|
||||
else {
|
||||
if (!exists(id, "FONT")) {
|
||||
Log.error('ZipFolderLibrary: Font at $id does not exist.');
|
||||
return null;
|
||||
}
|
||||
var e = Font.fromBytes(unzip(assets[_parsedAsset]));
|
||||
return e;
|
||||
}
|
||||
__parseAsset(id);
|
||||
var font = Font.fromBytes(unzip(assets[_parsedAsset]));
|
||||
|
||||
var openflFont = new OpenFLFont();
|
||||
@:privateAccess
|
||||
openflFont.__fromLimeFont(font);
|
||||
OpenFLFont.registerFont(openflFont);
|
||||
|
||||
return font;
|
||||
}
|
||||
public override function getImage(id:String):Image {
|
||||
__parseAsset(id);
|
||||
return Image.fromBytes(unzip(assets[_parsedAsset]));
|
||||
}
|
||||
|
||||
public override function getImage(id:String):Image {
|
||||
if (useImageCache && __isCacheValid(LimeAssets.cache.image, id))
|
||||
return LimeAssets.cache.image.get('$libName:$id');
|
||||
else {
|
||||
if (!exists(id, "IMAGE")) {
|
||||
Log.error('ZipFolderLibrary: Image at $id does not exist.');
|
||||
return null;
|
||||
}
|
||||
var e = Image.fromBytes(unzip(assets[_parsedAsset]));
|
||||
if (useImageCache) cachedImages.set(id, e);
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
@@ -134,8 +87,8 @@ class ZipFolderLibrary extends AssetLibrary implements ModsAssetLibrary {
|
||||
if (cache.exists(isLocal ? '$libName:$asset': asset)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public override function exists(asset:String, type:String):Bool {
|
||||
|
||||
public override function exists(asset:String, type:String):Bool {
|
||||
if(!__parseAsset(asset)) return false;
|
||||
|
||||
return assets[_parsedAsset] != null;
|
||||
@@ -147,10 +100,10 @@ class ZipFolderLibrary extends AssetLibrary implements ModsAssetLibrary {
|
||||
|
||||
public function getFiles(folder:String):Array<String> {
|
||||
var content:Array<String> = [];
|
||||
|
||||
|
||||
if (!folder.endsWith("/")) folder = folder + "/";
|
||||
if (!__parseAsset(folder)) return [];
|
||||
|
||||
|
||||
@:privateAccess
|
||||
for(k=>e in assets) {
|
||||
if (k.toLowerCase().startsWith(_parsedAsset)) {
|
||||
@@ -164,10 +117,10 @@ class ZipFolderLibrary extends AssetLibrary implements ModsAssetLibrary {
|
||||
|
||||
public function getFolders(folder:String):Array<String> {
|
||||
var content:Array<String> = [];
|
||||
|
||||
|
||||
if (!folder.endsWith("/")) folder = folder + "/";
|
||||
if (!__parseAsset(folder)) return [];
|
||||
|
||||
|
||||
@:privateAccess
|
||||
for(k=>e in assets) {
|
||||
if (k.toLowerCase().startsWith(_parsedAsset)) {
|
||||
@@ -178,5 +131,5 @@ class ZipFolderLibrary extends AssetLibrary implements ModsAssetLibrary {
|
||||
}
|
||||
return content;
|
||||
}
|
||||
#end
|
||||
}
|
||||
}
|
||||
#end
|
||||
@@ -168,11 +168,7 @@ class Paths
|
||||
|
||||
inline static public function font(key:String)
|
||||
{
|
||||
#if sys
|
||||
return sys.FileSystem.absolutePath(OpenFlAssets.getPath(getPath('fonts/$key', FONT, null)));
|
||||
#else
|
||||
return getPath('fonts/$key', FONT, null);
|
||||
#end
|
||||
}
|
||||
|
||||
inline static public function obj(key:String) {
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
package funkin.utils;
|
||||
|
||||
#if sys
|
||||
import haxe.io.Input;
|
||||
import haxe.zip.Entry;
|
||||
import haxe.zip.Reader;
|
||||
import sys.io.File;
|
||||
import sys.io.FileInput;
|
||||
import haxe.zip.InflateImpl;
|
||||
|
||||
/**
|
||||
* Class that extends Reader allowing you to load ZIP entries without blowing your RAM up!!
|
||||
* Half of the code is taken from haxe libraries btw
|
||||
*/
|
||||
class SysZip extends Reader {
|
||||
var input:Input;
|
||||
var fileInput:FileInput;
|
||||
|
||||
public var entries:List<SysZipEntry>;
|
||||
|
||||
/**
|
||||
* Opens a zip from a specified path.
|
||||
* @param path Path to the zip file.
|
||||
*/
|
||||
public static function openFromFile(path:String) {
|
||||
|
||||
return new SysZip(File.read(path, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SysZip from a specified file input.
|
||||
* @param input File input.
|
||||
*/
|
||||
public function new(input:FileInput) {
|
||||
super(input);
|
||||
fileInput = input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all the data present in a specified entry.
|
||||
* NOTE: If the entry is compressed, the data won't be decompressed. For decompression, use `unzipEntry`.
|
||||
* @param e Entry
|
||||
*/
|
||||
public function readEntryData(e:SysZipEntry) {
|
||||
var bytes:haxe.io.Bytes = null;
|
||||
var buf = null;
|
||||
var tmp = null;
|
||||
|
||||
fileInput.seek(e.seekPos, SeekBegin);
|
||||
if (e.crc32 == null) {
|
||||
if (e.compressed) {
|
||||
#if neko
|
||||
// enter progressive mode : we use a different input which has
|
||||
// a temporary buffer, this is necessary since we have to uncompress
|
||||
// progressively, and after that we might have pending read data
|
||||
// that needs to be processed
|
||||
var bufSize = 65536;
|
||||
if (buf == null) {
|
||||
buf = new haxe.io.BufferInput(i, haxe.io.Bytes.alloc(bufSize));
|
||||
tmp = haxe.io.Bytes.alloc(bufSize);
|
||||
i = buf;
|
||||
}
|
||||
var out = new haxe.io.BytesBuffer();
|
||||
var z = new neko.zip.Uncompress(-15);
|
||||
z.setFlushMode(neko.zip.Flush.SYNC);
|
||||
while (true) {
|
||||
if (buf.available == 0)
|
||||
buf.refill();
|
||||
var p = bufSize - buf.available;
|
||||
if (p != buf.pos) {
|
||||
// because of lack of "srcLen" in zip api, we need to always be stuck to the buffer end
|
||||
buf.buf.blit(p, buf.buf, buf.pos, buf.available);
|
||||
buf.pos = p;
|
||||
}
|
||||
var r = z.execute(buf.buf, buf.pos, tmp, 0);
|
||||
out.addBytes(tmp, 0, r.write);
|
||||
buf.pos += r.read;
|
||||
buf.available -= r.read;
|
||||
if (r.done)
|
||||
break;
|
||||
}
|
||||
bytes = out.getBytes();
|
||||
#else
|
||||
var bufSize = 65536;
|
||||
if (tmp == null)
|
||||
tmp = haxe.io.Bytes.alloc(bufSize);
|
||||
var out = new haxe.io.BytesBuffer();
|
||||
var z = new InflateImpl(i, false, false);
|
||||
while (true) {
|
||||
var n = z.readBytes(tmp, 0, bufSize);
|
||||
out.addBytes(tmp, 0, n);
|
||||
if (n < bufSize)
|
||||
break;
|
||||
}
|
||||
bytes = out.getBytes();
|
||||
#end
|
||||
} else
|
||||
bytes = i.read(e.dataSize);
|
||||
e.crc32 = i.readInt32();
|
||||
if (e.crc32 == 0x08074b50)
|
||||
e.crc32 = i.readInt32();
|
||||
e.dataSize = i.readInt32();
|
||||
e.fileSize = i.readInt32();
|
||||
// set data to uncompressed
|
||||
e.dataSize = e.fileSize;
|
||||
e.compressed = false;
|
||||
} else
|
||||
bytes = i.read(e.dataSize);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unzips and returns all of the data present in an entry.
|
||||
* @param f Entry to read from.
|
||||
*/
|
||||
public function unzipEntry(f:SysZipEntry) {
|
||||
var data = readEntryData(f);
|
||||
|
||||
if (!f.compressed)
|
||||
return data;
|
||||
var c = new haxe.zip.Uncompress(-15);
|
||||
var s = haxe.io.Bytes.alloc(f.fileSize);
|
||||
var r = c.execute(data, 0, s, 0);
|
||||
c.close();
|
||||
if (!r.done || r.read != data.length || r.write != f.fileSize)
|
||||
throw "Invalid compressed data for " + f.fileName;
|
||||
data = s;
|
||||
return data;
|
||||
}
|
||||
|
||||
public override function read():List<Entry> {
|
||||
if (entries != null)
|
||||
return entries;
|
||||
entries = new List();
|
||||
while (true) {
|
||||
var e = readEntryHeader();
|
||||
if (e == null)
|
||||
break;
|
||||
|
||||
var zipEntry:SysZipEntry = cast e;
|
||||
zipEntry.seekPos = fileInput.tell();
|
||||
entries.add(zipEntry);
|
||||
fileInput.seek(e.dataSize, SeekCur);
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
public function dispose() {
|
||||
if (input != null)
|
||||
input.close();
|
||||
}
|
||||
}
|
||||
|
||||
typedef SysZipEntry = {
|
||||
> Entry,
|
||||
var seekPos:Int;
|
||||
}
|
||||
#end
|
||||
Reference in New Issue
Block a user