Advertisement
Guest User

Untitled

a guest
Jun 17th, 2019
79
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.84 KB | None | 0 0
  1. package;
  2.  
  3. import haxe.io.Bytes;
  4. import lime.utils.UInt8Array;
  5. import lime.graphics.Image;
  6. import lime.graphics.ImageBuffer;
  7. import openfl.Assets.AssetLibrary;
  8. import openfl.Assets;
  9. import openfl.Lib;
  10. import openfl.utils.ByteArray;
  11.  
  12. /**
  13. * Loads images and text files from one big LZMA-compressed binary blob.
  14. * 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
  15. * @author hasufel, larsiusprime
  16. */
  17. class PakLibrary extends AssetLibrary
  18. {
  19. private var _images:Map<String, Image>;
  20. private var _strings:Map<String, String>;
  21.  
  22. public function new(header:String = "assets/header", pak:String = "assets/resources.pak") {
  23.  
  24. super();
  25. init(header, pak);
  26.  
  27. }
  28.  
  29. override public function unload():Void {
  30.  
  31. for (key in _images.keys())
  32. {
  33. _images.remove(key);
  34. }
  35.  
  36. for (key in _strings.keys())
  37. {
  38. _strings.remove(key);
  39. }
  40.  
  41. _strings = null;
  42.  
  43. }
  44.  
  45. override public function exists (id:String, type:String):Bool {
  46.  
  47. var b = false;
  48.  
  49. if (type == cast AssetType.IMAGE) {
  50.  
  51. b = _images != null && _images.exists(id);
  52.  
  53. }
  54. else if (type == cast AssetType.TEXT) {
  55.  
  56. b = _strings != null && _strings.exists(id);
  57.  
  58. }
  59. else if (type == cast AssetType.BINARY) {
  60.  
  61. b = (_images != null && _images.exists(id)) || (_strings != null && _strings.exists(id));
  62.  
  63. }
  64.  
  65. return b;
  66.  
  67. }
  68.  
  69. override public function getImage(id:String):Image {
  70.  
  71. if (_images == null) return null;
  72. return _images.get(id);
  73.  
  74. }
  75.  
  76. override public function getText(id:String):String {
  77.  
  78. if (_strings == null) return null;
  79. return _strings.get(id);
  80.  
  81. }
  82.  
  83. /**********PRIVATE**********/
  84.  
  85. //These temp structures are used to set up all the data and get it into ram, and are then destroyed
  86.  
  87. private static inline var PAK_ASSET_TEXT:Int = 0;
  88. private static inline var PAK_ASSET_IMAGE:Int = 1;
  89.  
  90. private var _tempHeaderBytes:Bytes;
  91. private var _tempAssetBytes:Bytes;
  92. private var _tempHeaderDetails:Array<Array<Int>>;
  93. private var _tempHeaderFilenames:Array<String>;
  94.  
  95. private var _headerSource:String;
  96. private var _pakSource:String;
  97.  
  98. private function init(header:String, pak:String):Void {
  99.  
  100. _pakSource = pak;
  101. _headerSource = header;
  102. _tempHeaderBytes = Assets.getBytes(header);
  103. _tempAssetBytes = Assets.getBytes(pak);
  104.  
  105. var fail = false;
  106.  
  107. if (_tempHeaderBytes == null) {
  108.  
  109. trace("[PakLibrary] Header asset \"" + header + "\" does not exist");
  110. fail = true;
  111.  
  112. }
  113. if (_tempAssetBytes == null) {
  114.  
  115. trace("[PakLibrary] Resource asset \"" + pak + "\" does not exist");
  116. fail = true;
  117.  
  118. }
  119.  
  120. if (!fail) {
  121.  
  122. makeHeaderDetails();
  123. loadAssetsFromPakIntoRam();
  124.  
  125. }
  126. }
  127.  
  128. private function makeHeaderDetails():Void {
  129.  
  130. //Detail entry formats are:
  131. // Image: [typeFlag, numBytes, width, height]
  132. // Text: [typeFlag, numBytes]
  133.  
  134. //Create two temporary data structures to hold our data:
  135. _tempHeaderDetails = [];
  136. _tempHeaderFilenames = [];
  137.  
  138. //Get the bytes from the header and uncompress them:
  139. var ba:ByteArray = ByteArray.fromBytes(_tempHeaderBytes);
  140. ba.uncompress(LZMA);
  141.  
  142. var howMany:Int = ba.readInt();
  143.  
  144. //March through the bytes one by one
  145. for (i in 0...howMany) {
  146.  
  147. var numBytes:Int = ba.readInt(); //Byte 0: number of bytes to read from _assetBytes
  148. var typeFlag:Int = ba.readInt(); //Byte 1: type flag
  149.  
  150. var details:Array<Int> = [typeFlag, numBytes];
  151.  
  152. if (typeFlag == PAK_ASSET_IMAGE) { //if it's an image:
  153.  
  154. var w:Int = ba.readInt(); //Byte 2: width of image
  155. var h:Int = ba.readInt(); //Byte 3: height of image
  156. details.push(w);
  157. details.push(h);
  158.  
  159. }
  160.  
  161. var filenameSize:Int = ba.readShort(); //Next Byte: size of filename
  162. var filenameText:String = ba.readUTFBytes(filenameSize); //Read that many bytes as the filename
  163.  
  164. //make sure it's a valid file type
  165. if (typeFlag == PAK_ASSET_IMAGE || typeFlag == PAK_ASSET_TEXT) {
  166.  
  167. _tempHeaderDetails.push(details);
  168. _tempHeaderFilenames.push(filenameText);
  169. }
  170. }
  171. }
  172.  
  173. private function loadAssetsFromPakIntoRam():Void {
  174.  
  175. //hasufel notes: this could be ported in multithreading. limit to a pool of 4 rolling threads (to match with cores of platforms)
  176.  
  177. _images = new Map<String, Image>();
  178. _strings = new Map<String, String>();
  179.  
  180. for (i in 0..._tempHeaderDetails.length) {
  181.  
  182. var details = _tempHeaderDetails[i];
  183. var filename = _tempHeaderFilenames[i];
  184.  
  185. //load the images and text files and store them in the permanent easy-access map structures
  186.  
  187. switch(details[0]) {
  188.  
  189. case PAK_ASSET_TEXT : _strings.set(filename, getTempPakText(i, details));
  190. case PAK_ASSET_IMAGE: _images.set(filename, getTempPakImage(i, details));
  191.  
  192. }
  193. }
  194.  
  195. //nullify temporary structures and assets from ram, we dont need references anymore
  196.  
  197. _tempAssetBytes = null;
  198. _tempHeaderBytes = null;
  199. _tempHeaderFilenames = null;
  200.  
  201. for (i in 0..._tempHeaderDetails.length) {
  202.  
  203. _tempHeaderDetails[i] = null;
  204.  
  205. }
  206. _tempHeaderDetails = null;
  207.  
  208. //clear the cache of the bytes we loaded to start this whole operation
  209. Assets.cache.clear(_headerSource);
  210. Assets.cache.clear(_pakSource);
  211. }
  212.  
  213. private function getTempPakText(n:Int, details:Array<Int>):String {
  214.  
  215. if (details == null) return null;
  216. if (details[0] != PAK_ASSET_TEXT) return null;
  217.  
  218. var bytes = getTempPakFileBytes(n);
  219. var byteArray = deflateBytes(bytes);
  220.  
  221. var str:String = null;
  222.  
  223. if (byteArray != null) {
  224.  
  225. var s:String = byteArray.toString();
  226. str = s;
  227.  
  228. }
  229.  
  230. bytes = null;
  231. byteArray = null;
  232.  
  233. return str;
  234. }
  235.  
  236. private function getTempPakImage(n:Int, details:Array<Int>):Image {
  237.  
  238. if (details == null) return null;
  239. if (details[0] != PAK_ASSET_IMAGE) return null;
  240.  
  241. var bytes = getTempPakFileBytes(n);
  242. var byteArray = deflateBytes(bytes);
  243.  
  244. var img:Image = null;
  245.  
  246. if (byteArray != null) {
  247.  
  248. //png, get dimensions:
  249. var w:Int = details[2];
  250. var h:Int = details[3];
  251.  
  252. var buffer = new ImageBuffer (new UInt8Array (w * h * 4), w, h);
  253. buffer.format = BGRA32;
  254. buffer.premultiplied = true;
  255.  
  256. img = new Image (buffer, 0, 0, w, h);
  257. img.setPixels(img.rect, byteArray, ARGB32);
  258. }
  259.  
  260. bytes = null;
  261. byteArray = null;
  262.  
  263. return img;
  264. }
  265.  
  266. private function getTempPakFileBytes(n:Int):Bytes {
  267.  
  268. if (n < 0 || _tempHeaderDetails.length <= n) return null;
  269.  
  270. var start:Int = 0;
  271.  
  272. //get to the correct starting byte offset
  273. for (i in 0...n) start += _tempHeaderDetails[i][1];
  274.  
  275. //allocate the correct number of bytes
  276. var b:Bytes = Bytes.alloc(_tempHeaderDetails[n][1]);
  277.  
  278. //fill the bytes from the pak file
  279. b.blit(0, _tempAssetBytes, start, _tempHeaderDetails[n][1]);
  280.  
  281. return b;
  282.  
  283. }
  284.  
  285. private inline function deflateBytes(b:Bytes):ByteArray {
  286.  
  287. //decompress the byte array
  288. var d:ByteArray = ByteArray.fromBytes(b);
  289. d.uncompress(LZMA);
  290. d.position = 0;
  291. return d;
  292.  
  293. }
  294. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement