Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package;
- import haxe.io.Bytes;
- import lime.utils.UInt8Array;
- import lime.graphics.Image;
- import lime.graphics.ImageBuffer;
- import openfl.Assets.AssetLibrary;
- import openfl.Assets;
- import openfl.Lib;
- import openfl.utils.ByteArray;
- /**
- * Loads images and text files from one big LZMA-compressed binary blob.
- * Assets load much faster than reading from disk using regular Assets.get(), but the entire contents of the PAK will stay in ram from the moment you create it
- * @author hasufel, larsiusprime
- */
- class PakLibrary extends AssetLibrary
- {
- private var _images:Map<String, Image>;
- private var _strings:Map<String, String>;
- public function new(header:String = "assets/header", pak:String = "assets/resources.pak") {
- super();
- init(header, pak);
- }
- override public function unload():Void {
- for (key in _images.keys())
- {
- _images.remove(key);
- }
- for (key in _strings.keys())
- {
- _strings.remove(key);
- }
- _strings = null;
- }
- override public function exists (id:String, type:String):Bool {
- var b = false;
- if (type == cast AssetType.IMAGE) {
- b = _images != null && _images.exists(id);
- }
- else if (type == cast AssetType.TEXT) {
- b = _strings != null && _strings.exists(id);
- }
- else if (type == cast AssetType.BINARY) {
- b = (_images != null && _images.exists(id)) || (_strings != null && _strings.exists(id));
- }
- return b;
- }
- override public function getImage(id:String):Image {
- if (_images == null) return null;
- return _images.get(id);
- }
- override public function getText(id:String):String {
- if (_strings == null) return null;
- return _strings.get(id);
- }
- /**********PRIVATE**********/
- //These temp structures are used to set up all the data and get it into ram, and are then destroyed
- private static inline var PAK_ASSET_TEXT:Int = 0;
- private static inline var PAK_ASSET_IMAGE:Int = 1;
- private var _tempHeaderBytes:Bytes;
- private var _tempAssetBytes:Bytes;
- private var _tempHeaderDetails:Array<Array<Int>>;
- private var _tempHeaderFilenames:Array<String>;
- private var _headerSource:String;
- private var _pakSource:String;
- private function init(header:String, pak:String):Void {
- _pakSource = pak;
- _headerSource = header;
- _tempHeaderBytes = Assets.getBytes(header);
- _tempAssetBytes = Assets.getBytes(pak);
- var fail = false;
- if (_tempHeaderBytes == null) {
- trace("[PakLibrary] Header asset \"" + header + "\" does not exist");
- fail = true;
- }
- if (_tempAssetBytes == null) {
- trace("[PakLibrary] Resource asset \"" + pak + "\" does not exist");
- fail = true;
- }
- if (!fail) {
- makeHeaderDetails();
- loadAssetsFromPakIntoRam();
- }
- }
- private function makeHeaderDetails():Void {
- //Detail entry formats are:
- // Image: [typeFlag, numBytes, width, height]
- // Text: [typeFlag, numBytes]
- //Create two temporary data structures to hold our data:
- _tempHeaderDetails = [];
- _tempHeaderFilenames = [];
- //Get the bytes from the header and uncompress them:
- var ba:ByteArray = ByteArray.fromBytes(_tempHeaderBytes);
- ba.uncompress(LZMA);
- var howMany:Int = ba.readInt();
- //March through the bytes one by one
- for (i in 0...howMany) {
- var numBytes:Int = ba.readInt(); //Byte 0: number of bytes to read from _assetBytes
- var typeFlag:Int = ba.readInt(); //Byte 1: type flag
- var details:Array<Int> = [typeFlag, numBytes];
- if (typeFlag == PAK_ASSET_IMAGE) { //if it's an image:
- var w:Int = ba.readInt(); //Byte 2: width of image
- var h:Int = ba.readInt(); //Byte 3: height of image
- details.push(w);
- details.push(h);
- }
- var filenameSize:Int = ba.readShort(); //Next Byte: size of filename
- var filenameText:String = ba.readUTFBytes(filenameSize); //Read that many bytes as the filename
- //make sure it's a valid file type
- if (typeFlag == PAK_ASSET_IMAGE || typeFlag == PAK_ASSET_TEXT) {
- _tempHeaderDetails.push(details);
- _tempHeaderFilenames.push(filenameText);
- }
- }
- }
- private function loadAssetsFromPakIntoRam():Void {
- //hasufel notes: this could be ported in multithreading. limit to a pool of 4 rolling threads (to match with cores of platforms)
- _images = new Map<String, Image>();
- _strings = new Map<String, String>();
- for (i in 0..._tempHeaderDetails.length) {
- var details = _tempHeaderDetails[i];
- var filename = _tempHeaderFilenames[i];
- //load the images and text files and store them in the permanent easy-access map structures
- switch(details[0]) {
- case PAK_ASSET_TEXT : _strings.set(filename, getTempPakText(i, details));
- case PAK_ASSET_IMAGE: _images.set(filename, getTempPakImage(i, details));
- }
- }
- //nullify temporary structures and assets from ram, we dont need references anymore
- _tempAssetBytes = null;
- _tempHeaderBytes = null;
- _tempHeaderFilenames = null;
- for (i in 0..._tempHeaderDetails.length) {
- _tempHeaderDetails[i] = null;
- }
- _tempHeaderDetails = null;
- //clear the cache of the bytes we loaded to start this whole operation
- Assets.cache.clear(_headerSource);
- Assets.cache.clear(_pakSource);
- }
- private function getTempPakText(n:Int, details:Array<Int>):String {
- if (details == null) return null;
- if (details[0] != PAK_ASSET_TEXT) return null;
- var bytes = getTempPakFileBytes(n);
- var byteArray = deflateBytes(bytes);
- var str:String = null;
- if (byteArray != null) {
- var s:String = byteArray.toString();
- str = s;
- }
- bytes = null;
- byteArray = null;
- return str;
- }
- private function getTempPakImage(n:Int, details:Array<Int>):Image {
- if (details == null) return null;
- if (details[0] != PAK_ASSET_IMAGE) return null;
- var bytes = getTempPakFileBytes(n);
- var byteArray = deflateBytes(bytes);
- var img:Image = null;
- if (byteArray != null) {
- //png, get dimensions:
- var w:Int = details[2];
- var h:Int = details[3];
- var buffer = new ImageBuffer (new UInt8Array (w * h * 4), w, h);
- buffer.format = BGRA32;
- buffer.premultiplied = true;
- img = new Image (buffer, 0, 0, w, h);
- img.setPixels(img.rect, byteArray, ARGB32);
- }
- bytes = null;
- byteArray = null;
- return img;
- }
- private function getTempPakFileBytes(n:Int):Bytes {
- if (n < 0 || _tempHeaderDetails.length <= n) return null;
- var start:Int = 0;
- //get to the correct starting byte offset
- for (i in 0...n) start += _tempHeaderDetails[i][1];
- //allocate the correct number of bytes
- var b:Bytes = Bytes.alloc(_tempHeaderDetails[n][1]);
- //fill the bytes from the pak file
- b.blit(0, _tempAssetBytes, start, _tempHeaderDetails[n][1]);
- return b;
- }
- private inline function deflateBytes(b:Bytes):ByteArray {
- //decompress the byte array
- var d:ByteArray = ByteArray.fromBytes(b);
- d.uncompress(LZMA);
- d.position = 0;
- return d;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement