diff --git a/haxe/Timer.hx b/haxe/Timer.hx
index d3055f99c..d4ffeca0b 100644
--- a/haxe/Timer.hx
+++ b/haxe/Timer.hx
@@ -47,7 +47,7 @@ class Timer {
#elseif java
private var timer : java.util.Timer;
private var task : java.util.TimerTask;
- #else
+ #elseif (haxe_ver >= "3.4.0")
private var event : MainLoop.MainEvent;
#end
@@ -72,7 +72,7 @@ class Timer {
#elseif java
timer = new java.util.Timer();
timer.scheduleAtFixedRate(task = new TimerTask(this), haxe.Int64.ofInt(time_ms), haxe.Int64.ofInt(time_ms));
- #else
+ #elseif (haxe_ver >= "3.4.0")
var dt = time_ms / 1000;
event = MainLoop.add(function() {
@:privateAccess event.nextRun += dt;
@@ -106,7 +106,7 @@ class Timer {
timer = null;
}
task = null;
- #else
+ #elseif (haxe_ver >= "3.4.0")
if( event != null ) {
event.stop();
event = null;
diff --git a/haxe/io/Bytes.hx b/haxe/io/Bytes.hx
new file mode 100644
index 000000000..e0e5ab620
--- /dev/null
+++ b/haxe/io/Bytes.hx
@@ -0,0 +1,814 @@
+/*
+ * Copyright (C)2005-2016 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+package haxe.io; #if !js
+
+
+#if cpp
+using cpp.NativeArray;
+#end
+
+#if !macro
+@:autoBuild(lime._macros.AssetsMacro.embedBytes()) // Enable @:bytes embed metadata
+#end
+
+class Bytes {
+
+ public var length(default,null) : Int;
+ var b : BytesData;
+
+ function new(length,b) {
+ this.length = length;
+ this.b = b;
+ #if flash
+ b.endian = flash.utils.Endian.LITTLE_ENDIAN;
+ #end
+ }
+
+ public inline function get( pos : Int ) : Int {
+ #if neko
+ return untyped $sget(b,pos);
+ #elseif flash
+ return b[pos];
+ #elseif php
+ return b.get(pos);
+ #elseif cpp
+ return untyped b[pos];
+ #elseif java
+ return untyped b[pos] & 0xFF;
+ #elseif python
+ return python.Syntax.arrayAccess(b, pos);
+ #else
+ return b[pos];
+ #end
+ }
+
+ public inline function set( pos : Int, v : Int ) : Void {
+ #if neko
+ untyped $sset(b,pos,v);
+ #elseif flash
+ b[pos] = v;
+ #elseif php
+ b.set(pos, v);
+ #elseif cpp
+ untyped b[pos] = v;
+ #elseif java
+ b[pos] = cast v;
+ #elseif cs
+ b[pos] = cast v;
+ #elseif python
+ python.Syntax.arraySet(b, pos, v & 0xFF);
+ #else
+ b[pos] = v & 0xFF;
+ #end
+ }
+
+ public function blit( pos : Int, src : Bytes, srcpos : Int, len : Int ) : Void {
+ #if !neko
+ if( pos < 0 || srcpos < 0 || len < 0 || pos + len > length || srcpos + len > src.length ) throw Error.OutsideBounds;
+ #end
+ #if neko
+ try untyped $sblit(b,pos,src.b,srcpos,len) catch( e : Dynamic ) throw Error.OutsideBounds;
+ #elseif php
+ b.blit(pos, src.b, srcpos, len);
+ #elseif flash
+ b.position = pos;
+ if( len > 0 ) b.writeBytes(src.b,srcpos,len);
+ #elseif java
+ java.lang.System.arraycopy(src.b, srcpos, b, pos, len);
+ #elseif cs
+ cs.system.Array.Copy(src.b, srcpos, b, pos, len);
+ #elseif python
+ python.Syntax.pythonCode("self.b[{0}:{0}+{1}] = src.b[srcpos:srcpos+{1}]", pos, len);
+ #elseif cpp
+ b.blit(pos, src.b, srcpos, len);
+ #else
+ var b1 = b;
+ var b2 = src.b;
+ if( b1 == b2 && pos > srcpos ) {
+ var i = len;
+ while( i > 0 ) {
+ i--;
+ b1[i + pos] = b2[i + srcpos];
+ }
+ return;
+ }
+ for( i in 0...len )
+ b1[i+pos] = b2[i+srcpos];
+ #end
+ }
+
+ public function fill( pos : Int, len : Int, value : Int ) {
+ #if flash
+ var v4 = value&0xFF;
+ v4 |= v4<<8;
+ v4 |= v4<<16;
+ b.position = pos;
+ for( i in 0...len>>2 )
+ b.writeUnsignedInt(v4);
+ pos += len&~3;
+ for( i in 0...len&3 )
+ set(pos++,value);
+ #elseif cpp
+ untyped __global__.__hxcpp_memory_memset(b,pos,len,value);
+ #else
+ for( i in 0...len )
+ set(pos++, value);
+ #end
+ }
+
+ public function sub( pos : Int, len : Int ) : Bytes {
+ #if !neko
+ if( pos < 0 || len < 0 || pos + len > length ) throw Error.OutsideBounds;
+ #end
+ #if neko
+ return try new Bytes(len,untyped __dollar__ssub(b,pos,len)) catch( e : Dynamic ) throw Error.OutsideBounds;
+ #elseif flash
+ b.position = pos;
+ var b2 = new flash.utils.ByteArray();
+ b.readBytes(b2,0,len);
+ return new Bytes(len,b2);
+ #elseif php
+ return new Bytes(len, b.sub(pos, len));
+ #elseif java
+ var newarr = new java.NativeArray(len);
+ java.lang.System.arraycopy(b, pos, newarr, 0, len);
+ return new Bytes(len, newarr);
+ #elseif cs
+ var newarr = new cs.NativeArray(len);
+ cs.system.Array.Copy(b, pos, newarr, 0, len);
+ return new Bytes(len, newarr);
+ #elseif python
+ return new Bytes(len, python.Syntax.arrayAccess(b, pos, pos+len) );
+ #else
+ return new Bytes(len,b.slice(pos,pos+len));
+ #end
+ }
+
+ public function compare( other : Bytes ) : Int {
+ #if neko
+ return untyped __dollar__compare(b,other.b);
+ #elseif flash
+ var len = (length < other.length) ? length : other.length;
+ var b1 = b;
+ var b2 = other.b;
+ b1.position = 0;
+ b2.position = 0;
+ b1.endian = flash.utils.Endian.BIG_ENDIAN;
+ b2.endian = flash.utils.Endian.BIG_ENDIAN;
+ for( i in 0...len>>2 )
+ if( b1.readUnsignedInt() != b2.readUnsignedInt() ) {
+ b1.position -= 4;
+ b2.position -= 4;
+ var d = b1.readUnsignedInt() - b2.readUnsignedInt();
+ b1.endian = flash.utils.Endian.LITTLE_ENDIAN;
+ b2.endian = flash.utils.Endian.LITTLE_ENDIAN;
+ return d;
+ }
+ for( i in 0...len & 3 )
+ if( b1.readUnsignedByte() != b2.readUnsignedByte() ) {
+ b1.endian = flash.utils.Endian.LITTLE_ENDIAN;
+ b2.endian = flash.utils.Endian.LITTLE_ENDIAN;
+ return b1[b1.position-1] - b2[b2.position-1];
+ }
+ b1.endian = flash.utils.Endian.LITTLE_ENDIAN;
+ b2.endian = flash.utils.Endian.LITTLE_ENDIAN;
+ return length - other.length;
+ #elseif php
+ return b.compare(other.b);
+ //#elseif cs
+ //TODO: memcmp if unsafe flag is on
+ #elseif cpp
+ return b.memcmp(other.b);
+ #else
+ var b1 = b;
+ var b2 = other.b;
+ var len = (length < other.length) ? length : other.length;
+ for( i in 0...len )
+ if( b1[i] != b2[i] )
+ return untyped b1[i] - untyped b2[i];
+ return length - other.length;
+ #end
+ }
+
+
+ /**
+ Returns the IEEE double precision value at given position (in low endian encoding).
+ Result is unspecified if reading outside of the bounds
+ **/
+ #if (neko_v21 || (cpp && !cppia) || flash) inline #end
+ public function getDouble( pos : Int ) : Float {
+ #if neko_v21
+ return untyped $sgetd(b, pos, false);
+ #elseif flash
+ b.position = pos;
+ return b.readDouble();
+ #elseif cpp
+ if( pos < 0 || pos + 8 > length ) throw Error.OutsideBounds;
+ return untyped __global__.__hxcpp_memory_get_double(b,pos);
+ #else
+ return FPHelper.i64ToDouble(getInt32(pos),getInt32(pos+4));
+ #end
+ }
+
+ /**
+ Returns the IEEE single precision value at given position (in low endian encoding).
+ Result is unspecified if reading outside of the bounds
+ **/
+ #if (neko_v21 || (cpp && !cppia) || flash) inline #end
+ public function getFloat( pos : Int ) : Float {
+ #if neko_v21
+ return untyped $sgetf(b, pos, false);
+ #elseif flash
+ b.position = pos;
+ return b.readFloat();
+ #elseif cpp
+ if( pos < 0 || pos + 4 > length ) throw Error.OutsideBounds;
+ return untyped __global__.__hxcpp_memory_get_float(b,pos);
+ #else
+ var b = new haxe.io.BytesInput(this,pos,4);
+ return b.readFloat();
+ #end
+ }
+
+ /**
+ Store the IEEE double precision value at given position in low endian encoding.
+ Result is unspecified if writing outside of the bounds.
+ **/
+ #if (neko_v21 || flash) inline #end
+ public function setDouble( pos : Int, v : Float ) : Void {
+ #if neko_v21
+ untyped $ssetd(b, pos, v, false);
+ #elseif neko
+ untyped $sblit(b, pos, FPHelper._double_bytes(v,false), 0, 8);
+ #elseif flash
+ b.position = pos;
+ b.writeDouble(v);
+ #elseif cpp
+ if( pos < 0 || pos + 8 > length ) throw Error.OutsideBounds;
+ untyped __global__.__hxcpp_memory_set_double(b,pos,v);
+ #else
+ var i = FPHelper.doubleToI64(v);
+ setInt32(pos, i.low);
+ setInt32(pos + 4, i.high);
+ #end
+ }
+
+ /**
+ Store the IEEE single precision value at given position in low endian encoding.
+ Result is unspecified if writing outside of the bounds.
+ **/
+ #if (neko_v21 || flash) inline #end
+ public function setFloat( pos : Int, v : Float ) : Void {
+ #if neko_v21
+ untyped $ssetf(b, pos, v, false);
+ #elseif neko
+ untyped $sblit(b, pos, FPHelper._float_bytes(v,false), 0, 4);
+ #elseif flash
+ b.position = pos;
+ b.writeFloat(v);
+ #elseif cpp
+ if( pos < 0 || pos + 4 > length ) throw Error.OutsideBounds;
+ untyped __global__.__hxcpp_memory_set_float(b,pos,v);
+ #else
+ setInt32(pos, FPHelper.floatToI32(v));
+ #end
+ }
+
+ /**
+ Returns the 16 bit unsigned integer at given position (in low endian encoding).
+ **/
+ public inline function getUInt16( pos : Int ) : Int {
+ #if neko_v21
+ return untyped $sget16(b, pos, false);
+ #else
+ return get(pos) | (get(pos + 1) << 8);
+ #end
+ }
+
+ /**
+ Store the 16 bit unsigned integer at given position (in low endian encoding).
+ **/
+ public inline function setUInt16( pos : Int, v : Int ) : Void {
+ #if neko_v21
+ untyped $sset16(b, pos, v, false);
+ #else
+ set(pos, v);
+ set(pos + 1, v >> 8);
+ #end
+ }
+
+ /**
+ Returns the 32 bit integer at given position (in low endian encoding).
+ **/
+ public inline function getInt32( pos : Int ) : Int {
+ #if neko_v21
+ return untyped $sget32(b, pos, false);
+ #elseif (php || python)
+ var v = get(pos) | (get(pos + 1) << 8) | (get(pos + 2) << 16) | (get(pos+3) << 24);
+ return if( v & 0x80000000 != 0 ) v | 0x80000000 else v;
+ #elseif lua
+ var v = get(pos) | (get(pos + 1) << 8) | (get(pos + 2) << 16) | (get(pos+3) << 24);
+ return lua.Boot.clamp(if( v & 0x80000000 != 0 ) v | 0x80000000 else v);
+ #else
+ return get(pos) | (get(pos + 1) << 8) | (get(pos + 2) << 16) | (get(pos+3) << 24);
+ #end
+ }
+
+ /**
+ Returns the 64 bit integer at given position (in low endian encoding).
+ **/
+ public inline function getInt64( pos : Int ) : haxe.Int64 {
+ return haxe.Int64.make(getInt32(pos+4),getInt32(pos));
+ }
+
+ /**
+ Store the 32 bit integer at given position (in low endian encoding).
+ **/
+ public inline function setInt32( pos : Int, v : Int ) : Void {
+ #if neko_v21
+ untyped $sset32(b, pos, v, false);
+ #else
+ set(pos, v);
+ set(pos + 1, v >> 8);
+ set(pos + 2, v >> 16);
+ set(pos + 3, v >>> 24);
+ #end
+ }
+
+ /**
+ Store the 64 bit integer at given position (in low endian encoding).
+ **/
+ public inline function setInt64( pos : Int, v : haxe.Int64 ) : Void {
+ setInt32(pos, v.low);
+ setInt32(pos + 4, v.high);
+ }
+
+ public function getString( pos : Int, len : Int ) : String {
+ #if !neko
+ if( pos < 0 || len < 0 || pos + len > length ) throw Error.OutsideBounds;
+ #end
+ #if neko
+ return try new String(untyped __dollar__ssub(b,pos,len)) catch( e : Dynamic ) throw Error.OutsideBounds;
+ #elseif flash
+ b.position = pos;
+ return b.readUTFBytes(len);
+ #elseif php
+ return b.getString(pos, len);
+ #elseif cpp
+ var result:String="";
+ untyped __global__.__hxcpp_string_of_bytes(b,result,pos,len);
+ return result;
+ #elseif cs
+ return cs.system.text.Encoding.UTF8.GetString(b, pos, len);
+ #elseif java
+ try
+ return new String(b, pos, len, "UTF-8")
+ catch (e:Dynamic) throw e;
+ #elseif python
+ return python.Syntax.pythonCode("self.b[{0}:{0}+{1}].decode('UTF-8','replace')", pos, len);
+ #elseif lua
+ var begin = cast(Math.min(pos,b.length),Int);
+ var end = cast(Math.min(pos+len,b.length),Int);
+ return [for (i in begin...end) String.fromCharCode(b[i])].join("");
+ #else
+ var s = "";
+ var b = b;
+ var fcc = String.fromCharCode;
+ var i = pos;
+ var max = pos+len;
+ // utf8-decode and utf16-encode
+ while( i < max ) {
+ var c = b[i++];
+ if( c < 0x80 ) {
+ if( c == 0 ) break;
+ s += fcc(c);
+ } else if( c < 0xE0 )
+ s += fcc( ((c & 0x3F) << 6) | (b[i++] & 0x7F) );
+ else if( c < 0xF0 ) {
+ var c2 = b[i++];
+ s += fcc( ((c & 0x1F) << 12) | ((c2 & 0x7F) << 6) | (b[i++] & 0x7F) );
+ } else {
+ var c2 = b[i++];
+ var c3 = b[i++];
+ var u = ((c & 0x0F) << 18) | ((c2 & 0x7F) << 12) | ((c3 & 0x7F) << 6) | (b[i++] & 0x7F);
+ // surrogate pair
+ s += fcc( (u >> 10) + 0xD7C0 );
+ s += fcc( (u & 0x3FF) | 0xDC00 );
+ }
+ }
+ return s;
+ #end
+ }
+
+ @:deprecated("readString is deprecated, use getString instead")
+ @:noCompletion
+ public inline function readString(pos:Int, len:Int):String {
+ return getString(pos, len);
+ }
+
+ public function toString() : String {
+ #if neko
+ return new String(untyped __dollar__ssub(b,0,length));
+ #elseif flash
+ b.position = 0;
+ return b.readUTFBytes(length);
+ #elseif php
+ return b.toString();
+ #elseif cs
+ return cs.system.text.Encoding.UTF8.GetString(b, 0, length);
+ #elseif java
+ try
+ {
+ return new String(b, 0, length, "UTF-8");
+ }
+ catch (e:Dynamic) throw e;
+ #else
+ return getString(0,length);
+ #end
+ }
+
+ public function toHex() : String {
+ var s = new StringBuf();
+ var chars = [];
+ var str = "0123456789abcdef";
+ for( i in 0...str.length )
+ chars.push(str.charCodeAt(i));
+ for( i in 0...length ) {
+ var c = get(i);
+ s.addChar(chars[c >> 4]);
+ s.addChar(chars[c & 15]);
+ }
+ return s.toString();
+ }
+
+ public inline function getData() : BytesData {
+ return b;
+ }
+
+ public static function alloc( length : Int ) : Bytes {
+ #if neko
+ return new Bytes(length,untyped __dollar__smake(length));
+ #elseif flash
+ var b = new flash.utils.ByteArray();
+ b.length = length;
+ return new Bytes(length,b);
+ #elseif php
+ return new Bytes(length, BytesData.alloc(length));
+ #elseif cpp
+ var a = new BytesData();
+ if (length>0) cpp.NativeArray.setSize(a, length);
+ return new Bytes(length, a);
+ #elseif cs
+ return new Bytes(length, new cs.NativeArray(length));
+ #elseif java
+ return new Bytes(length, new java.NativeArray(length));
+ #elseif python
+ return new Bytes(length, new python.Bytearray(length));
+ #else
+ var a = new Array();
+ for( i in 0...length )
+ a.push(0);
+ return new Bytes(length,a);
+ #end
+ }
+
+ @:pure
+ public static function ofString( s : String ) : Bytes {
+ #if neko
+ return new Bytes(s.length,untyped __dollar__ssub(s.__s,0,s.length));
+ #elseif flash
+ var b = new flash.utils.ByteArray();
+ b.writeUTFBytes(s);
+ return new Bytes(b.length,b);
+ #elseif php
+ var x = BytesData.ofString(s);
+ return new Bytes(x.length, x);
+ #elseif cpp
+ var a = new BytesData();
+ untyped __global__.__hxcpp_bytes_of_string(a,s);
+ return new Bytes(a.length, a);
+ #elseif cs
+ var b = cs.system.text.Encoding.UTF8.GetBytes(s);
+ return new Bytes(b.Length, b);
+ #elseif java
+ try
+ {
+ var b:BytesData = untyped s.getBytes("UTF-8");
+ return new Bytes(b.length, b);
+ }
+ catch (e:Dynamic) throw e;
+
+ #elseif python
+ var b:BytesData = new python.Bytearray(s, "UTF-8");
+ return new Bytes(b.length, b);
+
+ #elseif lua
+ var bytes = [for (c in 0...s.length) StringTools.fastCodeAt(s,c)];
+ return new Bytes(bytes.length, bytes);
+ #else
+ var a = new Array();
+ // utf16-decode and utf8-encode
+ var i = 0;
+ while( i < s.length ) {
+ var c : Int = StringTools.fastCodeAt(s,i++);
+ // surrogate pair
+ if( 0xD800 <= c && c <= 0xDBFF )
+ c = (c - 0xD7C0 << 10) | (StringTools.fastCodeAt(s,i++) & 0x3FF);
+ if( c <= 0x7F )
+ a.push(c);
+ else if( c <= 0x7FF ) {
+ a.push( 0xC0 | (c >> 6) );
+ a.push( 0x80 | (c & 63) );
+ } else if( c <= 0xFFFF ) {
+ a.push( 0xE0 | (c >> 12) );
+ a.push( 0x80 | ((c >> 6) & 63) );
+ a.push( 0x80 | (c & 63) );
+ } else {
+ a.push( 0xF0 | (c >> 18) );
+ a.push( 0x80 | ((c >> 12) & 63) );
+ a.push( 0x80 | ((c >> 6) & 63) );
+ a.push( 0x80 | (c & 63) );
+ }
+ }
+ return new Bytes(a.length,a);
+ #end
+ }
+
+ public static function ofData( b : BytesData ) {
+ #if flash
+ return new Bytes(b.length,b);
+ #elseif neko
+ return new Bytes(untyped __dollar__ssize(b),b);
+ #elseif php
+ return new Bytes(b.length, b);
+ #elseif cs
+ return new Bytes(b.Length,b);
+ #else
+ return new Bytes(b.length,b);
+ #end
+ }
+
+ /**
+ Read the most efficiently possible the n-th byte of the data.
+ Behavior when reading outside of the available data is unspecified.
+ **/
+ public inline static function fastGet( b : BytesData, pos : Int ) : Int {
+ #if neko
+ return untyped __dollar__sget(b,pos);
+ #elseif flash
+ return b[pos];
+ #elseif php
+ return b.get(pos);
+ #elseif cpp
+ return untyped b.unsafeGet(pos);
+ #elseif java
+ return untyped b[pos] & 0xFF;
+ #else
+ return b[pos];
+ #end
+ }
+
+}
+
+
+#else
+
+
+#if !nodejs
+import js.html.compat.Uint8Array;
+import js.html.compat.DataView;
+#end
+
+#if !macro
+@:autoBuild(lime._macros.AssetsMacro.embedBytes()) // Enable @:bytes embed metadata
+#end
+
+class Bytes {
+
+ public var length(default,null) : Int;
+ var b : js.html.Uint8Array;
+ var data : js.html.DataView;
+
+ function new(data:BytesData) {
+ this.length = data.byteLength;
+ this.b = new js.html.Uint8Array(data);
+ untyped {
+ b.bufferValue = data; // some impl does not return the same instance in .buffer
+ data.hxBytes = this;
+ data.bytes = this.b;
+ }
+ }
+
+ public inline function get( pos : Int ) : Int {
+ return b[pos];
+ }
+
+ public inline function set( pos : Int, v : Int ) : Void {
+ b[pos] = v & 0xFF; // the &0xFF is necessary for js.html.compat support
+ }
+
+ public function blit( pos : Int, src : Bytes, srcpos : Int, len : Int ) : Void {
+ if( pos < 0 || srcpos < 0 || len < 0 || pos + len > length || srcpos + len > src.length ) throw Error.OutsideBounds;
+ if( srcpos == 0 && len == src.b.byteLength )
+ b.set(src.b,pos);
+ else
+ b.set(src.b.subarray(srcpos,srcpos+len),pos);
+ }
+
+ public function fill( pos : Int, len : Int, value : Int ) : Void {
+ for( i in 0...len )
+ set(pos++, value);
+ }
+
+ public function sub( pos : Int, len : Int ) : Bytes {
+ if( pos < 0 || len < 0 || pos + len > length ) throw Error.OutsideBounds;
+ return new Bytes(b.buffer.slice(pos+b.byteOffset,pos+b.byteOffset+len));
+ }
+
+ public function compare( other : Bytes ) : Int {
+ var b1 = b;
+ var b2 = other.b;
+ var len = (length < other.length) ? length : other.length;
+ for( i in 0...len )
+ if( b1[i] != b2[i] )
+ return b1[i] - b2[i];
+ return length - other.length;
+ }
+
+ inline function initData() : Void {
+ if( data == null ) data = new js.html.DataView(b.buffer, b.byteOffset, b.byteLength);
+ }
+
+ public function getDouble( pos : Int ) : Float {
+ initData();
+ return data.getFloat64(pos, true);
+ }
+
+ public function getFloat( pos : Int ) : Float {
+ initData();
+ return data.getFloat32(pos, true);
+ }
+
+ public function setDouble( pos : Int, v : Float ) : Void {
+ initData();
+ data.setFloat64(pos, v, true);
+ }
+
+ public function setFloat( pos : Int, v : Float ) : Void {
+ initData();
+ data.setFloat32(pos, v, true);
+ }
+
+ public function getUInt16( pos : Int ) : Int {
+ initData();
+ return data.getUint16(pos, true);
+ }
+
+ public function setUInt16( pos : Int, v : Int ) : Void {
+ initData();
+ data.setUint16(pos, v, true);
+ }
+
+ public function getInt32( pos : Int ) : Int {
+ initData();
+ return data.getInt32(pos, true);
+ }
+
+ public function setInt32( pos : Int, v : Int ) : Void {
+ initData();
+ data.setInt32(pos, v, true);
+ }
+
+ public function getInt64( pos : Int ) : haxe.Int64 {
+ return Int64.make(getInt32(pos + 4),getInt32(pos));
+ }
+
+ public function setInt64( pos : Int, v : haxe.Int64 ) : Void {
+ setInt32(pos, v.low);
+ setInt32(pos + 4, v.high);
+ }
+
+ public function getString( pos : Int, len : Int ) : String {
+ if( pos < 0 || len < 0 || pos + len > length ) throw Error.OutsideBounds;
+ var s = "";
+ var b = b;
+ var fcc = String.fromCharCode;
+ var i = pos;
+ var max = pos+len;
+ // utf8-decode and utf16-encode
+ while( i < max ) {
+ var c = b[i++];
+ if( c < 0x80 ) {
+ if( c == 0 ) break;
+ s += fcc(c);
+ } else if( c < 0xE0 )
+ s += fcc( ((c & 0x3F) << 6) | (b[i++] & 0x7F) );
+ else if( c < 0xF0 ) {
+ var c2 = b[i++];
+ s += fcc( ((c & 0x1F) << 12) | ((c2 & 0x7F) << 6) | (b[i++] & 0x7F) );
+ } else {
+ var c2 = b[i++];
+ var c3 = b[i++];
+ var u = ((c & 0x0F) << 18) | ((c2 & 0x7F) << 12) | ((c3 & 0x7F) << 6) | (b[i++] & 0x7F);
+ // surrogate pair
+ s += fcc( (u >> 10) + 0xD7C0 );
+ s += fcc( (u & 0x3FF) | 0xDC00 );
+ }
+ }
+ return s;
+ }
+
+ @:deprecated("readString is deprecated, use getString instead")
+ @:noCompletion
+ public inline function readString(pos:Int, len:Int):String {
+ return getString(pos, len);
+ }
+
+ public function toString() : String {
+ return getString(0,length);
+ }
+
+ public function toHex() : String {
+ var s = new StringBuf();
+ var chars = [];
+ var str = "0123456789abcdef";
+ for( i in 0...str.length )
+ chars.push(str.charCodeAt(i));
+ for( i in 0...length ) {
+ var c = get(i);
+ s.addChar(chars[c >> 4]);
+ s.addChar(chars[c & 15]);
+ }
+ return s.toString();
+ }
+
+ public inline function getData() : BytesData {
+ return untyped b.bufferValue;
+ }
+
+ public static inline function alloc( length : Int ) : Bytes {
+ return new Bytes(new BytesData(length));
+ }
+
+ public static function ofString( s : String ) : Bytes {
+ var a = new Array();
+ // utf16-decode and utf8-encode
+ var i = 0;
+ while( i < s.length ) {
+ var c : Int = StringTools.fastCodeAt(s,i++);
+ // surrogate pair
+ if( 0xD800 <= c && c <= 0xDBFF )
+ c = (c - 0xD7C0 << 10) | (StringTools.fastCodeAt(s,i++) & 0x3FF);
+ if( c <= 0x7F )
+ a.push(c);
+ else if( c <= 0x7FF ) {
+ a.push( 0xC0 | (c >> 6) );
+ a.push( 0x80 | (c & 63) );
+ } else if( c <= 0xFFFF ) {
+ a.push( 0xE0 | (c >> 12) );
+ a.push( 0x80 | ((c >> 6) & 63) );
+ a.push( 0x80 | (c & 63) );
+ } else {
+ a.push( 0xF0 | (c >> 18) );
+ a.push( 0x80 | ((c >> 12) & 63) );
+ a.push( 0x80 | ((c >> 6) & 63) );
+ a.push( 0x80 | (c & 63) );
+ }
+ }
+ return new Bytes(new js.html.Uint8Array(a).buffer);
+ }
+
+ public static function ofData( b : BytesData ) : Bytes {
+ var hb = untyped b.hxBytes;
+ if( hb != null ) return hb;
+ return new Bytes(b);
+ }
+
+ public inline static function fastGet( b : BytesData, pos : Int ) : Int {
+ // this requires that we have wrapped it with haxe.io.Bytes beforehand
+ return untyped b.bytes[pos];
+ }
+
+}
+
+
+#end
\ No newline at end of file
diff --git a/lime/Assets.hx b/lime/Assets.hx
index d1902aa15..ae0c78e4e 100644
--- a/lime/Assets.hx
+++ b/lime/Assets.hx
@@ -1,1487 +1,4 @@
package lime;
-#if !macro
-
-
-import haxe.CallStack;
-import haxe.Json;
-import haxe.Unserializer;
-import lime.app.Event;
-import lime.app.Promise;
-import lime.app.Future;
-import lime.audio.AudioBuffer;
-import lime.graphics.Image;
-import lime.text.Font;
-import lime.utils.Bytes;
-import lime.utils.Log;
-
-
-/**
- *
The Assets class provides a cross-platform interface to access
- * embedded images, fonts, sounds and other resource files.
- *
- * The contents are populated automatically when an application
- * is compiled using the Lime command-line tools, based on the
- * contents of the *.xml project file.
- *
- * For most platforms, the assets are included in the same directory
- * or package as the application, and the paths are handled
- * automatically. For web content, the assets are preloaded before
- * the start of the rest of the application. You can customize the
- * preloader by extending the NMEPreloader class,
- * and specifying a custom preloader using
- * in the project file.
- */
-
-@:access(lime.AssetLibrary)
-
-
-class Assets {
-
-
- public static var cache = new AssetCache ();
- public static var libraries (default, null) = new Map ();
- public static var onChange = new EventVoid> ();
-
-
- public static function exists (id:String, type:AssetType = null):Bool {
-
- #if (tools && !display)
-
- if (type == null) {
-
- type = BINARY;
-
- }
-
- var libraryName = id.substring (0, id.indexOf (":"));
- var symbolName = id.substr (id.indexOf (":") + 1);
- var library = getLibrary (libraryName);
-
- if (library != null) {
-
- return library.exists (symbolName, cast type);
-
- }
-
- #end
-
- return false;
-
- }
-
-
- /**
- * Gets an instance of an embedded sound
- * @usage var sound = Assets.getSound("sound.wav");
- * @param id The ID or asset path for the sound
- * @return A new Sound object
- */
- public static function getAudioBuffer (id:String, useCache:Bool = true):AudioBuffer {
-
- #if (tools && !display)
-
- if (useCache && cache.enabled && cache.audio.exists (id)) {
-
- var audio = cache.audio.get (id);
-
- if (isValidAudio (audio)) {
-
- return audio;
-
- }
-
- }
-
- var libraryName = id.substring (0, id.indexOf (":"));
- var symbolName = id.substr (id.indexOf (":") + 1);
- var library = getLibrary (libraryName);
-
- if (library != null) {
-
- if (library.exists (symbolName, cast AssetType.SOUND)) {
-
- if (library.isLocal (symbolName, cast AssetType.SOUND)) {
-
- var audio = library.getAudioBuffer (symbolName);
-
- if (useCache && cache.enabled) {
-
- cache.audio.set (id, audio);
-
- }
-
- return audio;
-
- } else {
-
- Log.info ("Audio asset \"" + id + "\" exists, but only asynchronously");
-
- }
-
- } else {
-
- Log.info ("There is no audio asset with an ID of \"" + id + "\"");
-
- }
-
- } else {
-
- Log.info ("There is no asset library named \"" + libraryName + "\"");
-
- }
-
- #end
-
- return null;
-
- }
-
-
- /**
- * Gets an instance of an embedded binary asset
- * @usage var bytes = Assets.getBytes("file.zip");
- * @param id The ID or asset path for the file
- * @return A new Bytes object
- */
- public static function getBytes (id:String):Bytes {
-
- #if (tools && !display)
-
- var libraryName = id.substring (0, id.indexOf(":"));
- var symbolName = id.substr (id.indexOf(":") + 1);
- var library = getLibrary (libraryName);
-
- if (library != null) {
-
- if (library.exists (symbolName, cast AssetType.BINARY)) {
-
- if (library.isLocal (symbolName, cast AssetType.BINARY)) {
-
- return library.getBytes (symbolName);
-
- } else {
-
- Log.info ("String or Bytes asset \"" + id + "\" exists, but only asynchronously");
-
- }
-
- } else {
-
- Log.info ("There is no String or Bytes asset with an ID of \"" + id + "\"");
-
- }
-
- } else {
-
- Log.info ("There is no asset library named \"" + libraryName + "\"");
-
- }
-
- #end
-
- return null;
-
- }
-
-
- /**
- * Gets an instance of an embedded font
- * @usage var fontName = Assets.getFont("font.ttf").fontName;
- * @param id The ID or asset path for the font
- * @return A new Font object
- */
- public static function getFont (id:String, useCache:Bool = true):Font {
-
- #if (tools && !display)
-
- if (useCache && cache.enabled && cache.font.exists (id)) {
-
- return cache.font.get (id);
-
- }
-
- var libraryName = id.substring (0, id.indexOf (":"));
- var symbolName = id.substr (id.indexOf (":") + 1);
- var library = getLibrary (libraryName);
-
- if (library != null) {
-
- if (library.exists (symbolName, cast AssetType.FONT)) {
-
- if (library.isLocal (symbolName, cast AssetType.FONT)) {
-
- var font = library.getFont (symbolName);
-
- if (useCache && cache.enabled) {
-
- cache.font.set (id, font);
-
- }
-
- return font;
-
- } else {
-
- Log.info ("Font asset \"" + id + "\" exists, but only asynchronously");
-
- }
-
- } else {
-
- Log.info ("There is no Font asset with an ID of \"" + id + "\"");
-
- }
-
- } else {
-
- Log.info ("There is no asset library named \"" + libraryName + "\"");
-
- }
-
- #end
-
- return null;
-
- }
-
-
- /**
- * Gets an instance of an embedded bitmap
- * @usage var bitmap = new Bitmap(Assets.getBitmapData("image.jpg"));
- * @param id The ID or asset path for the bitmap
- * @param useCache (Optional) Whether to use BitmapData from the cache(Default: true)
- * @return A new BitmapData object
- */
- public static function getImage (id:String, useCache:Bool = true):Image {
-
- #if (tools && !display)
-
- if (useCache && cache.enabled && cache.image.exists (id)) {
-
- var image = cache.image.get (id);
-
- if (isValidImage (image)) {
-
- return image;
-
- }
-
- }
-
- var libraryName = id.substring (0, id.indexOf (":"));
- var symbolName = id.substr (id.indexOf (":") + 1);
- var library = getLibrary (libraryName);
-
- if (library != null) {
-
- if (library.exists (symbolName, cast AssetType.IMAGE)) {
-
- if (library.isLocal (symbolName, cast AssetType.IMAGE)) {
-
- var image = library.getImage (symbolName);
-
- if (useCache && cache.enabled) {
-
- cache.image.set (id, image);
-
- }
-
- return image;
-
- } else {
-
- Log.info ("Image asset \"" + id + "\" exists, but only asynchronously");
-
- }
-
- } else {
-
- Log.info ("There is no Image asset with an ID of \"" + id + "\"");
-
- }
-
- } else {
-
- Log.info ("There is no asset library named \"" + libraryName + "\"");
-
- }
-
- #end
-
- return null;
-
- }
-
-
- private static function getLibrary (name:String):AssetLibrary {
-
- if (name == null || name == "") {
-
- name = "default";
-
- }
-
- return libraries.get (name);
-
- }
-
-
- /**
- * Gets the file path (if available) for an asset
- * @usage var path = Assets.getPath("image.jpg");
- * @param id The ID or asset path for the asset
- * @return The path to the asset (or null)
- */
- public static function getPath (id:String):String {
-
- #if (tools && !display)
-
- var libraryName = id.substring (0, id.indexOf (":"));
- var symbolName = id.substr (id.indexOf (":") + 1);
- var library = getLibrary (libraryName);
-
- if (library != null) {
-
- if (library.exists (symbolName, null)) {
-
- return library.getPath (symbolName);
-
- } else {
-
- Log.info ("There is no asset with an ID of \"" + id + "\"");
-
- }
-
- } else {
-
- Log.info ("There is no asset library named \"" + libraryName + "\"");
-
- }
-
- #end
-
- return null;
-
- }
-
-
- /**
- * Gets an instance of an embedded text asset
- * @usage var text = Assets.getText("text.txt");
- * @param id The ID or asset path for the file
- * @return A new String object
- */
- public static function getText (id:String):String {
-
- #if (tools && !display)
-
- var libraryName = id.substring (0, id.indexOf(":"));
- var symbolName = id.substr (id.indexOf(":") + 1);
- var library = getLibrary (libraryName);
-
- if (library != null) {
-
- if (library.exists (symbolName, cast AssetType.TEXT)) {
-
- if (library.isLocal (symbolName, cast AssetType.TEXT)) {
-
- return library.getText (symbolName);
-
- } else {
-
- Log.info ("String asset \"" + id + "\" exists, but only asynchronously");
-
- }
-
- } else {
-
- Log.info ("There is no String asset with an ID of \"" + id + "\"");
-
- }
-
- } else {
-
- Log.info ("There is no asset library named \"" + libraryName + "\"");
-
- }
-
- #end
-
- return null;
-
- }
-
-
- public static function isLocal (id:String, type:AssetType = null, useCache:Bool = true):Bool {
-
- #if (tools && !display)
-
- if (useCache && cache.enabled) {
-
- if (type == AssetType.IMAGE || type == null) {
-
- if (cache.image.exists (id)) return true;
-
- }
-
- if (type == AssetType.FONT || type == null) {
-
- if (cache.font.exists (id)) return true;
-
- }
-
- if (type == AssetType.SOUND || type == AssetType.MUSIC || type == null) {
-
- if (cache.audio.exists (id)) return true;
-
- }
-
- }
-
- var libraryName = id.substring (0, id.indexOf (":"));
- var symbolName = id.substr (id.indexOf (":") + 1);
- var library = getLibrary (libraryName);
-
- if (library != null) {
-
- return library.isLocal (symbolName, cast type);
-
- }
-
- #end
-
- return false;
-
- }
-
-
- private static function isValidAudio (buffer:AudioBuffer):Bool {
-
- #if (tools && !display)
-
- return (buffer != null);
- //return (sound.__handle != null && sound.__handle != 0);
-
- #else
-
- return true;
-
- #end
-
- }
-
-
- private static function isValidImage (buffer:Image):Bool {
-
- #if (tools && !display)
- #if lime_cffi
-
- return (buffer != null);
- //return (bitmapData.__handle != null);
-
- #elseif flash
-
- /*try {
-
- image.bytes.width;
-
- } catch (e:Dynamic) {
-
- return false;
-
- }*/
- return (buffer != null);
-
- #end
- #end
-
- return true;
-
- }
-
-
- public static function list (type:AssetType = null):Array {
-
- var items = [];
-
- for (library in libraries) {
-
- var libraryItems = library.list (cast type);
-
- if (libraryItems != null) {
-
- items = items.concat (libraryItems);
-
- }
-
- }
-
- return items;
-
- }
-
-
- public static function loadAudioBuffer (id:String, useCache:Bool = true):Future {
-
- var promise = new Promise ();
-
- #if (tools && !display)
-
- if (useCache && cache.enabled && cache.audio.exists (id)) {
-
- var audio = cache.audio.get (id);
-
- if (isValidAudio (audio)) {
-
- promise.complete (audio);
- return promise.future;
-
- }
-
- }
-
- var libraryName = id.substring (0, id.indexOf (":"));
- var symbolName = id.substr (id.indexOf (":") + 1);
- var library = getLibrary (libraryName);
-
- if (library != null) {
-
- if (library.exists (symbolName, cast AssetType.SOUND)) {
-
- var future = library.loadAudioBuffer (symbolName);
-
- if (useCache && cache.enabled) {
-
- future.onComplete (function (audio) cache.audio.set (id, audio));
-
- }
-
- promise.completeWith (future);
-
- } else {
-
- promise.error ("[Assets] There is no audio asset with an ID of \"" + id + "\"");
-
- }
-
- } else {
-
- promise.error ("[Assets] There is no asset library named \"" + libraryName + "\"");
-
- }
-
- #end
-
- return promise.future;
-
- }
-
-
- public static function loadBytes (id:String):Future {
-
- var promise = new Promise ();
-
- #if (tools && !display)
-
- var libraryName = id.substring (0, id.indexOf (":"));
- var symbolName = id.substr (id.indexOf (":") + 1);
- var library = getLibrary (libraryName);
-
- if (library != null) {
-
- if (library.exists (symbolName, cast AssetType.BINARY)) {
-
- promise.completeWith (library.loadBytes (symbolName));
-
- } else {
-
- promise.error ("[Assets] There is no String or Bytes asset with an ID of \"" + id + "\"");
-
- }
-
- } else {
-
- promise.error ("[Assets] There is no asset library named \"" + libraryName + "\"");
-
- }
-
- #end
-
- return promise.future;
-
- }
-
-
- public static function loadFont (id:String):Future {
-
- var promise = new Promise ();
-
- #if (tools && !display)
-
- var libraryName = id.substring (0, id.indexOf (":"));
- var symbolName = id.substr (id.indexOf (":") + 1);
- var library = getLibrary (libraryName);
-
- if (library != null) {
-
- if (library.exists (symbolName, cast AssetType.FONT)) {
-
- promise.completeWith (library.loadFont (symbolName));
-
- } else {
-
- promise.error ("[Assets] There is no Font asset with an ID of \"" + id + "\"");
-
- }
-
- } else {
-
- promise.error ("[Assets] There is no asset library named \"" + libraryName + "\"");
-
- }
-
- #end
-
- return promise.future;
-
- }
-
-
- public static function loadImage (id:String, useCache:Bool = true):Future {
-
- var promise = new Promise ();
-
- #if (tools && !display)
-
- if (useCache && cache.enabled && cache.image.exists (id)) {
-
- var image = cache.image.get (id);
-
- if (isValidImage (image)) {
-
- promise.complete (image);
- return promise.future;
-
- }
-
- }
-
- var libraryName = id.substring (0, id.indexOf (":"));
- var symbolName = id.substr (id.indexOf (":") + 1);
- var library = getLibrary (libraryName);
-
- if (library != null) {
-
- if (library.exists (symbolName, cast AssetType.IMAGE)) {
-
- var future = library.loadImage (symbolName);
-
- if (useCache && cache.enabled) {
-
- future.onComplete (function (image) cache.image.set (id, image));
-
- }
-
- promise.completeWith (future);
-
- } else {
-
- promise.error ("[Assets] There is no Image asset with an ID of \"" + id + "\"");
-
- }
-
- } else {
-
- promise.error ("[Assets] There is no asset library named \"" + libraryName + "\"");
-
- }
-
- #end
-
- return promise.future;
-
- }
-
-
- public static function loadLibrary (name:String):Future {
-
- var promise = new Promise ();
-
- #if (tools && !display)
-
- var data = getText ("libraries/" + name + ".json");
-
- if (data != null && data != "") {
-
- var info = Json.parse (data);
- var library = Type.createInstance (Type.resolveClass (info.type), info.args);
- libraries.set (name, library);
- library.onChange.add (onChange.dispatch);
- promise.completeWith (library.load ());
-
- } else {
-
- promise.error ("[Assets] There is no asset library named \"" + name + "\"");
-
- }
-
- #end
-
- return promise.future;
-
- }
-
-
- public static function loadText (id:String):Future {
-
- var promise = new Promise ();
-
- #if (tools && !display)
-
- var libraryName = id.substring (0, id.indexOf (":"));
- var symbolName = id.substr (id.indexOf (":") + 1);
- var library = getLibrary (libraryName);
-
- if (library != null) {
-
- if (library.exists (symbolName, cast AssetType.TEXT)) {
-
- promise.completeWith (library.loadText (symbolName));
-
- } else {
-
- promise.error ("[Assets] There is no String asset with an ID of \"" + id + "\"");
-
- }
-
- } else {
-
- promise.error ("[Assets] There is no asset library named \"" + libraryName + "\"");
-
- }
-
- #end
-
- return promise.future;
-
- }
-
-
- public static function registerLibrary (name:String, library:AssetLibrary):Void {
-
- if (libraries.exists (name)) {
-
- if (libraries.get (name) == library) {
-
- return;
-
- } else {
-
- unloadLibrary (name);
-
- }
-
- }
-
- if (library != null) {
-
- library.onChange.add (library_onChange);
-
- }
-
- libraries.set (name, library);
-
- }
-
-
- public static function unloadLibrary (name:String):Void {
-
- #if (tools && !display)
-
- var library = libraries.get (name);
-
- if (library != null) {
-
- cache.clear (name + ":");
- library.onChange.remove (library_onChange);
- library.unload ();
-
- }
-
- libraries.remove (name);
-
- #end
-
- }
-
-
-
-
- // Event Handlers
-
-
-
-
- private static function library_onChange ():Void {
-
- cache.clear ();
- onChange.dispatch ();
-
- }
-
-
-}
-
-
-class AssetLibrary {
-
-
- public var onChange = new EventVoid> ();
-
-
- public function new () {
-
-
-
- }
-
-
- public function exists (id:String, type:String):Bool {
-
- return false;
-
- }
-
-
- public function getAudioBuffer (id:String):AudioBuffer {
-
- return null;
-
- }
-
-
- public function getBytes (id:String):Bytes {
-
- return null;
-
- }
-
-
- public function getFont (id:String):Font {
-
- return null;
-
- }
-
-
- public function getImage (id:String):Image {
-
- return null;
-
- }
-
-
- public function getPath (id:String):String {
-
- return null;
-
- }
-
-
- public function getText (id:String):String {
-
- #if (tools && !display)
-
- var bytes = getBytes (id);
-
- if (bytes == null) {
-
- return null;
-
- } else {
-
- return bytes.getString (0, bytes.length);
-
- }
-
- #else
-
- return null;
-
- #end
-
- }
-
-
- public function isLocal (id:String, type:String):Bool {
-
- return true;
-
- }
-
-
- public function list (type:String):Array {
-
- return null;
-
- }
-
-
- private function load ():Future {
-
- return new Future (function () return this);
-
- }
-
-
- public function loadAudioBuffer (id:String):Future {
-
- return new Future (function () return getAudioBuffer (id), true);
-
- }
-
-
- public function loadBytes (id:String):Future {
-
- return new Future (function () return getBytes (id), true);
-
- }
-
-
- public function loadFont (id:String):Future {
-
- return new Future (function () return getFont (id), true);
-
- }
-
-
- public function loadImage (id:String):Future {
-
- return new Future (function () return getImage (id), true);
-
- }
-
-
- public function loadText (id:String):Future {
-
- return loadBytes (id).then (function (bytes) {
-
- return new Future (function () {
-
- if (bytes == null) {
-
- return null;
-
- } else {
-
- return bytes.getString (0, bytes.length);
-
- }
-
- }, true);
-
- });
-
- }
-
-
- public function unload ():Void {
-
-
-
- }
-
-
-}
-
-
-class AssetCache {
-
-
- public var audio:Map;
- public var enabled:Bool = true;
- public var image:Map;
- public var font:Map;
- public var version:Int;
-
-
- public function new () {
-
- audio = new Map ();
- font = new Map ();
- image = new Map ();
- version = AssetCache.cacheVersion ();
-
- }
-
-
- private macro static function cacheVersion () {}
-
-
- public function clear (prefix:String = null):Void {
-
- if (prefix == null) {
-
- audio = new Map ();
- font = new Map ();
- image = new Map ();
-
- } else {
-
- var keys = audio.keys ();
-
- for (key in keys) {
-
- if (StringTools.startsWith (key, prefix)) {
-
- audio.remove (key);
-
- }
-
- }
-
- var keys = font.keys ();
-
- for (key in keys) {
-
- if (StringTools.startsWith (key, prefix)) {
-
- font.remove (key);
-
- }
-
- }
-
- var keys = image.keys ();
-
- for (key in keys) {
-
- if (StringTools.startsWith (key, prefix)) {
-
- image.remove (key);
-
- }
-
- }
-
- }
-
- }
-
-
-}
-
-
-#else
-
-
-import haxe.crypto.BaseCode;
-import haxe.io.Bytes;
-import haxe.macro.Context;
-import haxe.macro.Expr;
-import haxe.macro.Type;
-import haxe.Serializer;
-import lime.graphics.ImageBuffer;
-import sys.io.File;
-
-
-class Assets {
-
-
- private static var base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- private static var base64Encoder:BaseCode;
-
-
- private static function base64Encode (bytes:Bytes):String {
-
- var extension = switch (bytes.length % 3) {
-
- case 1: "==";
- case 2: "=";
- default: "";
-
- }
-
- if (base64Encoder == null) {
-
- base64Encoder = new BaseCode (Bytes.ofString (base64Chars));
-
- }
-
- return base64Encoder.encodeBytes (bytes).toString () + extension;
-
- }
-
-
- macro public static function embedBytes ():Array {
-
- var fields = embedData (":file");
-
- if (fields != null) {
-
- var constructor = macro {
-
- #if lime_console
- throw "not implemented";
- #else
- var bytes = haxe.Resource.getBytes (resourceName);
- super (bytes.length, bytes.b);
- #end
-
- };
-
- var args = [ { name: "length", opt: false, type: macro :Int }, { name: "bytesData", opt: false, type: macro :haxe.io.BytesData } ];
- fields.push ({ name: "new", access: [ APublic ], kind: FFun({ args: args, expr: constructor, params: [], ret: null }), pos: Context.currentPos () });
-
- }
-
- return fields;
-
- }
-
-
- macro public static function embedByteArray ():Array {
-
- var fields = embedData (":file");
-
- if (fields != null) {
-
- var constructor = macro {
-
- super ();
-
- #if lime_console
- throw "not implemented";
- #else
- var bytes = haxe.Resource.getBytes (resourceName);
- __fromBytes (bytes);
- #end
-
- };
-
- var args = [ { name: "length", opt: true, type: macro :Int, value: macro 0 } ];
- fields.push ({ name: "new", access: [ APublic ], kind: FFun({ args: args, expr: constructor, params: [], ret: null }), pos: Context.currentPos () });
-
- }
-
- return fields;
-
- }
-
-
- private static function embedData (metaName:String, encode:Bool = false):Array {
-
- var classType = Context.getLocalClass().get();
- var metaData = classType.meta.get();
- var position = Context.currentPos();
- var fields = Context.getBuildFields();
-
- for (meta in metaData) {
-
- if (meta.name == metaName) {
-
- if (meta.params.length > 0) {
-
- switch (meta.params[0].expr) {
-
- case EConst(CString(filePath)):
-
- #if lime_console
-
- var fieldValue = {
- pos: position,
- expr: EConst(CString(filePath))
- };
- fields.push ({
- kind: FVar(macro :String, fieldValue),
- name: "filePath",
- access: [ APrivate, AStatic ],
- pos: position
- });
-
- #else
-
- var path = filePath;
- if (!sys.FileSystem.exists(filePath)) {
- path = Context.resolvePath (filePath);
- }
- var bytes = File.getBytes (path);
- var resourceName = "__ASSET__" + metaName + "_" + (classType.pack.length > 0 ? classType.pack.join ("_") + "_" : "") + classType.name;
-
- if (encode) {
-
- var resourceType = "image/png";
-
- if (bytes.get (0) == 0xFF && bytes.get (1) == 0xD8) {
-
- resourceType = "image/jpg";
-
- } else if (bytes.get (0) == 0x47 && bytes.get (1) == 0x49 && bytes.get (2) == 0x46) {
-
- resourceType = "image/gif";
-
- }
-
- var fieldValue = { pos: position, expr: EConst(CString(resourceType)) };
- fields.push ({ kind: FVar(macro :String, fieldValue), name: "resourceType", access: [ APrivate, AStatic ], pos: position });
-
- var base64 = base64Encode (bytes);
- Context.addResource (resourceName, Bytes.ofString (base64));
-
- } else {
-
- Context.addResource (resourceName, bytes);
-
- }
-
- var fieldValue = { pos: position, expr: EConst(CString(resourceName)) };
- fields.push ({ kind: FVar(macro :String, fieldValue), name: "resourceName", access: [ APrivate, AStatic ], pos: position });
-
- #end
-
- return fields;
-
- default:
-
- }
-
- }
-
- }
-
- }
-
- return null;
-
- }
-
-
- macro public static function embedFont ():Array {
-
- var fields = null;
-
- var classType = Context.getLocalClass().get();
- var metaData = classType.meta.get();
- var position = Context.currentPos();
- var fields = Context.getBuildFields();
-
- var path = "";
- var glyphs = "32-255";
-
- for (meta in metaData) {
-
- if (meta.name == ":font") {
-
- if (meta.params.length > 0) {
-
- switch (meta.params[0].expr) {
-
- case EConst(CString(filePath)):
-
- path = filePath;
-
- if (!sys.FileSystem.exists(filePath)) {
-
- path = Context.resolvePath (filePath);
-
- }
-
- default:
-
- }
-
- }
-
- }
-
- }
-
- if (path != null && path != "") {
-
- #if lime_console
- throw "not implemented";
- #end
-
- #if html5
- Sys.command ("haxelib", [ "run", "lime", "generate", "-font-hash", sys.FileSystem.fullPath(path) ]);
- path += ".hash";
- #end
-
- var bytes = File.getBytes (path);
- var resourceName = "LIME_font_" + (classType.pack.length > 0 ? classType.pack.join ("_") + "_" : "") + classType.name;
-
- Context.addResource (resourceName, bytes);
-
- for (field in fields) {
-
- if (field.name == "new") {
-
- fields.remove (field);
- break;
-
- }
-
- }
-
- var fieldValue = { pos: position, expr: EConst(CString(resourceName)) };
- fields.push ({ kind: FVar(macro :String, fieldValue), name: "resourceName", access: [ APublic, AStatic ], pos: position });
-
- var constructor = macro {
-
- super ();
-
- __fromBytes (haxe.Resource.getBytes (resourceName));
-
- };
-
- fields.push ({ name: "new", access: [ APublic ], kind: FFun({ args: [], expr: constructor, params: [], ret: null }), pos: Context.currentPos() });
-
- return fields;
-
- }
-
- return fields;
-
- }
-
-
- macro public static function embedImage ():Array {
-
- #if html5
- var fields = embedData (":image", true);
- #else
- var fields = embedData (":image");
- #end
-
- if (fields != null) {
-
- var constructor = macro {
-
- #if html5
-
- super ();
-
- if (preload != null) {
-
- var buffer = new lime.graphics.ImageBuffer ();
- buffer.__srcImage = preload;
- buffer.width = preload.width;
- buffer.width = preload.height;
-
- __fromImageBuffer (buffer);
-
- } else {
-
- __fromBase64 (haxe.Resource.getString (resourceName), resourceType, function (image) {
-
- if (preload == null) {
-
- preload = image.buffer.__srcImage;
-
- }
-
- if (onload != null) {
-
- onload (image);
-
- }
-
- });
-
- }
-
- #else
-
- super ();
-
- #if lime_console
- throw "not implemented";
- #else
- __fromBytes (haxe.Resource.getBytes (resourceName), null);
- #end
-
- #end
-
- };
-
- var args = [ { name: "buffer", opt: true, type: macro :lime.graphics.ImageBuffer, value: null }, { name: "offsetX", opt: true, type: macro :Int, value: null }, { name: "offsetY", opt: true, type: macro :Int, value: null }, { name: "width", opt: true, type: macro :Int, value: null }, { name: "height", opt: true, type: macro :Int, value: null }, { name: "color", opt: true, type: macro :Null, value: null }, { name: "type", opt: true, type: macro :lime.graphics.ImageType, value: null } ];
-
- #if html5
- args.push ({ name: "onload", opt: true, type: macro :Dynamic, value: null });
- fields.push ({ kind: FVar(macro :js.html.Image, null), name: "preload", doc: null, meta: [], access: [ APublic, AStatic ], pos: Context.currentPos() });
- #end
-
- fields.push ({ name: "new", access: [ APublic ], kind: FFun({ args: args, expr: constructor, params: [], ret: null }), pos: Context.currentPos() });
-
- }
-
- return fields;
-
- }
-
-
- macro public static function embedSound ():Array {
-
- var fields = embedData (":sound");
-
- if (fields != null) {
-
- #if (openfl && !html5) // CFFILoader.h(248) : NOT Implemented:api_buffer_data
-
- var constructor = macro {
-
- super();
-
- #if lime_console
- throw "not implemented";
- #else
- var byteArray = openfl.utils.ByteArray.fromBytes (haxe.Resource.getBytes(resourceName));
- loadCompressedDataFromByteArray(byteArray, byteArray.length, forcePlayAsMusic);
- #end
-
- };
-
- var args = [ { name: "stream", opt: true, type: macro :openfl.net.URLRequest, value: null }, { name: "context", opt: true, type: macro :openfl.media.SoundLoaderContext, value: null }, { name: "forcePlayAsMusic", opt: true, type: macro :Bool, value: macro false } ];
- fields.push ({ name: "new", access: [ APublic ], kind: FFun({ args: args, expr: constructor, params: [], ret: null }), pos: Context.currentPos() });
-
- #end
-
- }
-
- return fields;
-
- }
-
-
-}
-
-
-class AssetCache {
-
-
- private static macro function cacheVersion () {
-
- return macro $v{ Std.int (Math.random () * 1000000) };
-
- }
-
-
-}
-
-
-#end
-
-
-@:enum abstract AssetType(String) {
-
- var BINARY = "BINARY";
- var FONT = "FONT";
- var IMAGE = "IMAGE";
- var MUSIC = "MUSIC";
- var SOUND = "SOUND";
- var TEMPLATE = "TEMPLATE";
- var TEXT = "TEXT";
-
-}
\ No newline at end of file
+typedef Assets = lime.utils.Assets;
\ No newline at end of file
diff --git a/lime/_backend/native/NativeApplication.hx b/lime/_backend/native/NativeApplication.hx
index 1994d81bd..3e503cb6f 100644
--- a/lime/_backend/native/NativeApplication.hx
+++ b/lime/_backend/native/NativeApplication.hx
@@ -597,6 +597,7 @@ class NativeApplication {
private function updateTimer ():Void {
+ #if lime_cffi
if (Timer.sRunningTimers.length > 0) {
var currentTime = System.getTimer ();
@@ -631,6 +632,7 @@ class NativeApplication {
}
}
+ #end
}
diff --git a/lime/_backend/native/NativeHTTPRequest.hx b/lime/_backend/native/NativeHTTPRequest.hx
index 3559dfe96..868ea08cd 100644
--- a/lime/_backend/native/NativeHTTPRequest.hx
+++ b/lime/_backend/native/NativeHTTPRequest.hx
@@ -81,7 +81,7 @@ class NativeHTTPRequest {
}
- bytes = lime.utils.Bytes.readFile (path);
+ bytes = lime.utils.Bytes.fromFile (path);
promise.complete (bytes);
});
diff --git a/lime/_backend/native/NativeRenderer.hx b/lime/_backend/native/NativeRenderer.hx
index 86d63fa59..48ce30299 100644
--- a/lime/_backend/native/NativeRenderer.hx
+++ b/lime/_backend/native/NativeRenderer.hx
@@ -132,10 +132,12 @@ class NativeRenderer {
var imageBuffer:ImageBuffer = null;
+ #if !macro
var data:Dynamic = lime_renderer_read_pixels (handle, rect);
if (data != null) {
imageBuffer = new ImageBuffer (new UInt8Array (@:privateAccess new Bytes (data.data.length, data.data.b)), data.width, data.height, data.bitsPerPixel);
}
+ #end
if (imageBuffer != null) {
diff --git a/lime/_backend/native/NativeWindow.hx b/lime/_backend/native/NativeWindow.hx
index 13ccf3e0f..c15d158ba 100644
--- a/lime/_backend/native/NativeWindow.hx
+++ b/lime/_backend/native/NativeWindow.hx
@@ -150,6 +150,7 @@ class NativeWindow {
if (handle != null) {
+ #if !macro
var index = lime_window_get_display (handle);
if (index > -1) {
@@ -157,6 +158,7 @@ class NativeWindow {
return System.getDisplay (index);
}
+ #end
}
@@ -241,12 +243,12 @@ class NativeWindow {
#if !macro
value = lime_window_set_fullscreen (handle, value);
- #end
parent.__width = lime_window_get_width (handle);
parent.__height = lime_window_get_height (handle);
parent.__x = lime_window_get_x (handle);
parent.__y = lime_window_get_y (handle);
+ #end
if (value) {
diff --git a/lime/_macros/AssetsMacro.hx b/lime/_macros/AssetsMacro.hx
new file mode 100644
index 000000000..705dbdc13
--- /dev/null
+++ b/lime/_macros/AssetsMacro.hx
@@ -0,0 +1,385 @@
+package lime._macros; #if macro
+
+
+import haxe.crypto.BaseCode;
+import haxe.io.Bytes;
+import haxe.macro.Context;
+import haxe.macro.Expr;
+import haxe.macro.Type;
+import haxe.Serializer;
+import lime.graphics.ImageBuffer;
+import sys.io.File;
+
+
+class AssetsMacro {
+
+
+ private static var base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ private static var base64Encoder:BaseCode;
+
+
+ private static function base64Encode (bytes:Bytes):String {
+
+ var extension = switch (bytes.length % 3) {
+
+ case 1: "==";
+ case 2: "=";
+ default: "";
+
+ }
+
+ if (base64Encoder == null) {
+
+ base64Encoder = new BaseCode (Bytes.ofString (base64Chars));
+
+ }
+
+ return base64Encoder.encodeBytes (bytes).toString () + extension;
+
+ }
+
+
+ macro public static function embedBytes ():Array {
+
+ var fields = embedData (":file");
+
+ if (fields != null) {
+
+ var constructor = macro {
+
+ #if lime_console
+ throw "not implemented";
+ #else
+ var bytes = haxe.Resource.getBytes (resourceName);
+ super (bytes.length, bytes.b);
+ #end
+
+ };
+
+ var args = [ { name: "length", opt: false, type: macro :Int }, { name: "bytesData", opt: false, type: macro :haxe.io.BytesData } ];
+ fields.push ({ name: "new", access: [ APublic ], kind: FFun({ args: args, expr: constructor, params: [], ret: null }), pos: Context.currentPos () });
+
+ }
+
+ return fields;
+
+ }
+
+
+ macro public static function embedByteArray ():Array {
+
+ var fields = embedData (":file");
+
+ if (fields != null) {
+
+ var constructor = macro {
+
+ super ();
+
+ #if lime_console
+ throw "not implemented";
+ #else
+ var bytes = haxe.Resource.getBytes (resourceName);
+ __fromBytes (bytes);
+ #end
+
+ };
+
+ var args = [ { name: "length", opt: true, type: macro :Int, value: macro 0 } ];
+ fields.push ({ name: "new", access: [ APublic ], kind: FFun({ args: args, expr: constructor, params: [], ret: null }), pos: Context.currentPos () });
+
+ }
+
+ return fields;
+
+ }
+
+
+ private static function embedData (metaName:String, encode:Bool = false):Array {
+
+ var classType = Context.getLocalClass().get();
+ var metaData = classType.meta.get();
+ var position = Context.currentPos();
+ var fields = Context.getBuildFields();
+
+ for (meta in metaData) {
+
+ if (meta.name == metaName) {
+
+ if (meta.params.length > 0) {
+
+ switch (meta.params[0].expr) {
+
+ case EConst(CString(filePath)):
+
+ #if lime_console
+
+ var fieldValue = {
+ pos: position,
+ expr: EConst(CString(filePath))
+ };
+ fields.push ({
+ kind: FVar(macro :String, fieldValue),
+ name: "filePath",
+ access: [ APrivate, AStatic ],
+ pos: position
+ });
+
+ #else
+
+ var path = filePath;
+ if (!sys.FileSystem.exists(filePath)) {
+ path = Context.resolvePath (filePath);
+ }
+ var bytes = File.getBytes (path);
+ var resourceName = "__ASSET__" + metaName + "_" + (classType.pack.length > 0 ? classType.pack.join ("_") + "_" : "") + classType.name;
+
+ if (encode) {
+
+ var resourceType = "image/png";
+
+ if (bytes.get (0) == 0xFF && bytes.get (1) == 0xD8) {
+
+ resourceType = "image/jpg";
+
+ } else if (bytes.get (0) == 0x47 && bytes.get (1) == 0x49 && bytes.get (2) == 0x46) {
+
+ resourceType = "image/gif";
+
+ }
+
+ var fieldValue = { pos: position, expr: EConst(CString(resourceType)) };
+ fields.push ({ kind: FVar(macro :String, fieldValue), name: "resourceType", access: [ APrivate, AStatic ], pos: position });
+
+ var base64 = base64Encode (bytes);
+ Context.addResource (resourceName, Bytes.ofString (base64));
+
+ } else {
+
+ Context.addResource (resourceName, bytes);
+
+ }
+
+ var fieldValue = { pos: position, expr: EConst(CString(resourceName)) };
+ fields.push ({ kind: FVar(macro :String, fieldValue), name: "resourceName", access: [ APrivate, AStatic ], pos: position });
+
+ #end
+
+ return fields;
+
+ default:
+
+ }
+
+ }
+
+ }
+
+ }
+
+ return null;
+
+ }
+
+
+ macro public static function embedFont ():Array {
+
+ var fields = null;
+
+ var classType = Context.getLocalClass().get();
+ var metaData = classType.meta.get();
+ var position = Context.currentPos();
+ var fields = Context.getBuildFields();
+
+ var path = "";
+ var glyphs = "32-255";
+
+ for (meta in metaData) {
+
+ if (meta.name == ":font") {
+
+ if (meta.params.length > 0) {
+
+ switch (meta.params[0].expr) {
+
+ case EConst(CString(filePath)):
+
+ path = filePath;
+
+ if (!sys.FileSystem.exists(filePath)) {
+
+ path = Context.resolvePath (filePath);
+
+ }
+
+ default:
+
+ }
+
+ }
+
+ }
+
+ }
+
+ if (path != null && path != "") {
+
+ #if lime_console
+ throw "not implemented";
+ #end
+
+ #if html5
+ Sys.command ("haxelib", [ "run", "lime", "generate", "-font-hash", sys.FileSystem.fullPath(path) ]);
+ path += ".hash";
+ #end
+
+ var bytes = File.getBytes (path);
+ var resourceName = "LIME_font_" + (classType.pack.length > 0 ? classType.pack.join ("_") + "_" : "") + classType.name;
+
+ Context.addResource (resourceName, bytes);
+
+ for (field in fields) {
+
+ if (field.name == "new") {
+
+ fields.remove (field);
+ break;
+
+ }
+
+ }
+
+ var fieldValue = { pos: position, expr: EConst(CString(resourceName)) };
+ fields.push ({ kind: FVar(macro :String, fieldValue), name: "resourceName", access: [ APublic, AStatic ], pos: position });
+
+ var constructor = macro {
+
+ super ();
+
+ __fromBytes (haxe.Resource.getBytes (resourceName));
+
+ };
+
+ fields.push ({ name: "new", access: [ APublic ], kind: FFun({ args: [], expr: constructor, params: [], ret: null }), pos: Context.currentPos() });
+
+ return fields;
+
+ }
+
+ return fields;
+
+ }
+
+
+ macro public static function embedImage ():Array {
+
+ #if html5
+ var fields = embedData (":image", true);
+ #else
+ var fields = embedData (":image");
+ #end
+
+ if (fields != null) {
+
+ var constructor = macro {
+
+ #if html5
+
+ super ();
+
+ if (preload != null) {
+
+ var buffer = new lime.graphics.ImageBuffer ();
+ buffer.__srcImage = preload;
+ buffer.width = preload.width;
+ buffer.width = preload.height;
+
+ __fromImageBuffer (buffer);
+
+ } else {
+
+ __fromBase64 (haxe.Resource.getString (resourceName), resourceType, function (image) {
+
+ if (preload == null) {
+
+ preload = image.buffer.__srcImage;
+
+ }
+
+ if (onload != null) {
+
+ onload (image);
+
+ }
+
+ });
+
+ }
+
+ #else
+
+ super ();
+
+ #if lime_console
+ throw "not implemented";
+ #else
+ __fromBytes (haxe.Resource.getBytes (resourceName), null);
+ #end
+
+ #end
+
+ };
+
+ var args = [ { name: "buffer", opt: true, type: macro :lime.graphics.ImageBuffer, value: null }, { name: "offsetX", opt: true, type: macro :Int, value: null }, { name: "offsetY", opt: true, type: macro :Int, value: null }, { name: "width", opt: true, type: macro :Int, value: null }, { name: "height", opt: true, type: macro :Int, value: null }, { name: "color", opt: true, type: macro :Null, value: null }, { name: "type", opt: true, type: macro :lime.graphics.ImageType, value: null } ];
+
+ #if html5
+ args.push ({ name: "onload", opt: true, type: macro :Dynamic, value: null });
+ fields.push ({ kind: FVar(macro :js.html.Image, null), name: "preload", doc: null, meta: [], access: [ APublic, AStatic ], pos: Context.currentPos() });
+ #end
+
+ fields.push ({ name: "new", access: [ APublic ], kind: FFun({ args: args, expr: constructor, params: [], ret: null }), pos: Context.currentPos() });
+
+ }
+
+ return fields;
+
+ }
+
+
+ macro public static function embedSound ():Array {
+
+ var fields = embedData (":sound");
+
+ if (fields != null) {
+
+ #if (openfl && !html5) // CFFILoader.h(248) : NOT Implemented:api_buffer_data
+
+ var constructor = macro {
+
+ super();
+
+ #if lime_console
+ throw "not implemented";
+ #else
+ var byteArray = openfl.utils.ByteArray.fromBytes (haxe.Resource.getBytes(resourceName));
+ loadCompressedDataFromByteArray(byteArray, byteArray.length, forcePlayAsMusic);
+ #end
+
+ };
+
+ var args = [ { name: "stream", opt: true, type: macro :openfl.net.URLRequest, value: null }, { name: "context", opt: true, type: macro :openfl.media.SoundLoaderContext, value: null }, { name: "forcePlayAsMusic", opt: true, type: macro :Bool, value: macro false } ];
+ fields.push ({ name: "new", access: [ APublic ], kind: FFun({ args: args, expr: constructor, params: [], ret: null }), pos: Context.currentPos() });
+
+ #end
+
+ }
+
+ return fields;
+
+ }
+
+
+}
+
+
+#end
\ No newline at end of file
diff --git a/lime/_macros/CFFIMacro.hx b/lime/_macros/CFFIMacro.hx
new file mode 100644
index 000000000..ceacc0fb0
--- /dev/null
+++ b/lime/_macros/CFFIMacro.hx
@@ -0,0 +1,337 @@
+package lime._macros; #if macro
+
+
+import haxe.macro.Context;
+import haxe.macro.Expr;
+import haxe.macro.Type;
+
+using haxe.macro.ComplexTypeTools;
+using haxe.macro.ExprTools;
+using haxe.macro.TypeTools;
+
+
+class CFFIMacro {
+
+
+ public static function build (defaultLibrary:String = "lime"):Array {
+
+ var pos = Context.currentPos ();
+ var fields = Context.getBuildFields ();
+ var newFields:Array = [];
+
+ for (field in fields) {
+
+ switch (field) {
+
+ case _ => { kind: FFun (fun), meta: meta } :
+
+ for (m in meta) {
+
+ if (m.name == ":cffi") {
+
+ var library = null;
+ var method = null;
+ var lazy = false;
+
+ if (Reflect.hasField (m, "params")) {
+
+ if (m.params.length > 0) library = m.params[0].getValue ();
+ if (m.params.length > 1) method = m.params[1].getValue ();
+ if (m.params.length > 2) lazy = m.params[2].getValue ();
+
+ }
+
+ if (library == null || library == "") {
+
+ library = defaultLibrary;
+
+ }
+
+ if (method == null || method == "") {
+
+ method = field.name;
+
+ }
+
+ var typeArgs = [];
+
+ for (arg in fun.args) {
+
+ typeArgs.push ( { name: arg.name, opt: false, t: arg.type.toType () } );
+
+ }
+
+ var type = __getFunctionType (typeArgs, fun.ret.toType ());
+
+ var typeString = type.string;
+ var typeSignature = type.signature;
+ var expr = "";
+
+ if (Context.defined ("display") || Context.defined ("disable_cffi")) {
+
+ switch (type.result.toString ()) {
+
+ case "Int", "Float":
+
+ expr += "return 0";
+
+ case "Bool":
+
+ expr += "return false";
+
+ default:
+
+ expr += "return null";
+
+ }
+
+ } else {
+
+ var cffiName = "cffi_" + field.name;
+ var cffiExpr, cffiType;
+
+ if (Context.defined ("cpp")) {
+
+ cffiExpr = 'new cpp.Callable<$typeString> (cpp.Prime._loadPrime ("$library", "$method", "$typeSignature", $lazy))';
+
+ } else {
+
+ var args = typeSignature.length - 1;
+
+ if (args > 5) {
+
+ args = -1;
+
+ }
+
+ cffiExpr = 'new cpp.Callable<$typeString> (lime.system.CFFI.load ("$library", "$method", $args, $lazy))';
+
+ }
+
+ cffiType = TPath ( { pack: [ "cpp" ], name: "Callable", params: [ TPType (TFun (type.args, type.result).toComplexType ()) ] } );
+
+ newFields.push ( { name: cffiName, access: [ APrivate, AStatic ], kind: FieldType.FVar (cffiType, Context.parse (cffiExpr, field.pos)), pos: field.pos } );
+
+ if (type.result.toString () != "Void" && type.result.toString () != "cpp.Void") {
+
+ expr += "return ";
+
+ }
+
+ expr += '$cffiName.call (';
+
+ for (i in 0...type.args.length) {
+
+ if (i > 0) expr += ", ";
+ expr += type.args[i].name;
+
+ }
+
+ expr += ")";
+
+ }
+
+ field.access.push (AInline);
+ fun.expr = Context.parse (expr, field.pos);
+
+ }
+
+ }
+
+ default:
+
+ }
+
+ }
+
+ fields = fields.concat (newFields);
+ return fields;
+
+ }
+
+
+ private static function __getFunctionType (args:Array<{ name : String, opt : Bool, t : Type }>, result:Type) {
+
+ #if (!disable_cffi && !display)
+ var useCPPTypes = Context.defined ("cpp");
+ #else
+ var useCPPTypes = false;
+ #end
+
+ var typeArgs = [];
+ var typeResult = null;
+ var typeSignature = "";
+
+ for (arg in args) {
+
+ switch (arg.t.toString ()) {
+
+ case "Int", "cpp.Int16", "cpp.Int32":
+
+ typeArgs.push (arg);
+ typeSignature += "i";
+
+ case "Bool":
+
+ typeArgs.push (arg);
+ typeSignature += "b";
+
+ case "cpp.Float32":
+
+ if (useCPPTypes) {
+
+ typeArgs.push ( { name: arg.name, opt: false, t: (macro :cpp.Float32).toType () } );
+
+ } else {
+
+ typeArgs.push (arg);
+
+ }
+
+ typeSignature += "f";
+
+ case "Float", "cpp.Float64":
+
+ typeArgs.push (arg);
+ typeSignature += "d";
+
+ case "String":
+
+ typeArgs.push (arg);
+ typeSignature += "s";
+
+ case "cpp.ConstCharStar":
+
+ typeArgs.push (arg);
+ typeSignature += "c";
+
+ case "Void", "cpp.Void":
+
+ if (useCPPTypes) {
+
+ typeArgs.push ( { name: arg.name, opt: false, t: (macro :cpp.Void).toType () } );
+
+ } else {
+
+ typeArgs.push (arg);
+
+ }
+
+ typeSignature += "v";
+
+ default:
+
+ if (useCPPTypes) {
+
+ typeArgs.push ( { name: arg.name, opt: false, t: (macro :cpp.Object).toType () } );
+
+ } else {
+
+ typeArgs.push ( { name: arg.name, opt: false, t: (macro :Dynamic).toType () } );
+
+ }
+
+ typeSignature += "o";
+
+ }
+
+ }
+
+ switch (result.toString ()) {
+
+ case "Int", "cpp.Int16", "cpp.Int32":
+
+ typeResult = result;
+ typeSignature += "i";
+
+ case "Bool":
+
+ typeResult = result;
+ typeSignature += "b";
+
+ case "cpp.Float32":
+
+ if (useCPPTypes) {
+
+ typeResult = (macro :cpp.Float32).toType ();
+
+ } else {
+
+ typeResult = result;
+
+ }
+
+ typeSignature += "f";
+
+ case "Float", "cpp.Float64":
+
+ typeResult = result;
+ typeSignature += "d";
+
+ case "String":
+
+ typeResult = result;
+ typeSignature += "s";
+
+ case "cpp.ConstCharStar":
+
+ typeResult = result;
+ typeSignature += "c";
+
+ case "Void", "cpp.Void":
+
+ if (useCPPTypes) {
+
+ typeResult = (macro :cpp.Void).toType ();
+
+ } else {
+
+ typeResult = result;
+
+ }
+
+ typeSignature += "v";
+
+ default:
+
+ if (useCPPTypes) {
+
+ typeResult = (macro :cpp.Object).toType ();
+
+ } else {
+
+ typeResult = (macro :Dynamic).toType ();
+
+ }
+
+ typeSignature += "o";
+
+ }
+
+ var typeString = "";
+
+ if (typeArgs.length == 0) {
+
+ typeString = "Void->";
+
+ } else {
+
+ for (arg in typeArgs) {
+
+ typeString += arg.t.toString () + "->";
+
+ }
+
+ }
+
+ typeString += typeResult.toString ();
+
+ return { args: typeArgs, result: typeResult, string: typeString, signature: typeSignature };
+
+ }
+
+
+}
+
+
+#end
\ No newline at end of file
diff --git a/lime/_macros/EventMacro.hx b/lime/_macros/EventMacro.hx
new file mode 100644
index 000000000..e44822682
--- /dev/null
+++ b/lime/_macros/EventMacro.hx
@@ -0,0 +1,167 @@
+package lime._macros; #if macro
+
+
+import haxe.macro.Context;
+import haxe.macro.Expr;
+import haxe.macro.Type;
+
+using haxe.macro.Tools;
+
+
+class EventMacro {
+
+
+ public static function build () {
+
+ var typeArgs;
+ var typeResult;
+
+ switch (Context.follow (Context.getLocalType ())) {
+
+ case TInst (_, [ Context.follow (_) => TFun (args, result) ]):
+
+ typeArgs = args;
+ typeResult = result;
+
+ case TInst (localType, _):
+
+ Context.fatalError ("Invalid number of type parameters for " + localType.toString (), Context.currentPos ());
+ return null;
+
+ default:
+
+ throw false;
+
+ }
+
+ var typeParam = TFun (typeArgs, typeResult);
+ var typeString = "";
+
+ if (typeArgs.length == 0) {
+
+ typeString = "Void->";
+
+ } else {
+
+ for (arg in typeArgs) {
+
+ typeString += arg.t.toString () + "->";
+
+ }
+
+ }
+
+ typeString += typeResult.toString ();
+ typeString = StringTools.replace (typeString, "->", "_");
+ typeString = StringTools.replace (typeString, ".", "_");
+ typeString = StringTools.replace (typeString, "<", "_");
+ typeString = StringTools.replace (typeString, ">", "_");
+
+ var name = "_Event_" + typeString;
+
+ try {
+
+ Context.getType ("lime.app." + name);
+
+ } catch (e:Dynamic) {
+
+ var pos = Context.currentPos ();
+ var fields = Context.getBuildFields ();
+
+ var args:Array = [];
+ var argName, argNames = [];
+
+ for (i in 0...typeArgs.length) {
+
+ if (i == 0) {
+
+ argName = "a";
+
+ } else {
+
+ argName = "a" + i;
+
+ }
+
+ argNames.push (Context.parse (argName, pos));
+ args.push ({ name: argName, type: typeArgs[i].t.toComplexType () });
+
+ }
+
+ var dispatch = macro {
+
+ canceled = false;
+
+ var listeners = __listeners;
+ var repeat = __repeat;
+ var i = 0;
+
+ while (i < listeners.length) {
+
+ listeners[i] ($a{argNames});
+
+ if (!repeat[i]) {
+
+ this.remove (cast listeners[i]);
+
+ } else {
+
+ i++;
+
+ }
+
+ if (canceled) {
+
+ break;
+
+ }
+
+ }
+
+ }
+
+ var i = 0;
+ var field;
+
+ while (i < fields.length) {
+
+ field = fields[i];
+
+ if (field.name == "__listeners" || field.name == "dispatch") {
+
+ fields.remove (field);
+
+ } else {
+
+ i++;
+
+ }
+
+ }
+
+ fields.push ( { name: "__listeners", access: [ APublic ], kind: FVar (TPath ({ pack: [], name: "Array", params: [ TPType (typeParam.toComplexType ()) ] })), pos: pos } );
+ fields.push ( { name: "dispatch", access: [ APublic ], kind: FFun ( { args: args, expr: dispatch, params: [], ret: macro :Void } ), pos: pos } );
+
+ Context.defineType ({
+
+ pos: pos,
+ pack: [ "lime", "app" ],
+ name: name,
+ kind: TDClass (),
+ fields: fields,
+ params: [ { name: "T" } ],
+ meta: [ { name: ":dox", params: [ macro hide ], pos: pos }, { name: ":noCompletion", pos: pos } ]
+
+ });
+
+ }
+
+ return TPath ( { pack: [ "lime", "app" ], name: name, params: [ TPType (typeParam.toComplexType ()) ] } ).toType ();
+
+ }
+
+
+}
+
+
+#end
\ No newline at end of file
diff --git a/lime/_macros/HTTPRequestMacro.hx b/lime/_macros/HTTPRequestMacro.hx
new file mode 100644
index 000000000..e1769adee
--- /dev/null
+++ b/lime/_macros/HTTPRequestMacro.hx
@@ -0,0 +1,211 @@
+package lime._macros; #if macro
+
+
+import haxe.macro.Context;
+import haxe.macro.Expr;
+import haxe.macro.Type;
+using haxe.macro.Tools;
+
+
+class HTTPRequestMacro {
+
+
+ private static function build () {
+
+ var paramType;
+ var type:BaseType, typeArgs;
+ var stringAbstract = false;
+ var bytesAbstract = false;
+
+ switch (Context.follow (Context.getLocalType ())) {
+
+ case TInst (localType, [ t ]):
+
+ paramType = t;
+
+ switch (t) {
+
+ case TInst (t, args):
+
+ type = t.get ();
+ typeArgs = args;
+
+ case TAbstract (t, args):
+
+ type = t.get ();
+ typeArgs = args;
+
+ stringAbstract = isStringAbstract (t.get ());
+ if (!stringAbstract) bytesAbstract = isBytesAbstract (t.get ());
+
+ case TType (t, args):
+
+ type = t.get ();
+ typeArgs = args;
+
+ case TMono (_):
+
+ Context.fatalError ("Invalid number of type parameters for " + localType.toString (), Context.currentPos ());
+ return null;
+
+ case TDynamic (_):
+
+ switch (Context.getType ("haxe.io.Bytes")) {
+
+ case TInst (t, args):
+
+ type = t.get ();
+ typeArgs = args;
+
+ default:
+
+ throw false;
+
+ }
+
+ default:
+
+ throw false;
+
+ }
+
+ default:
+
+ throw false;
+
+ }
+
+ var typeString = type.module;
+
+ if (type.name != type.module && !StringTools.endsWith (type.module, "." + type.name)) {
+
+ typeString += "." + type.name;
+
+ }
+
+ if (typeString == "String" || stringAbstract) {
+
+ return TPath ( { pack: [ "lime", "net" ], name: "HTTPRequest", sub: "_HTTPRequest_String", params: [ TPType (paramType.toComplexType ()) ] } ).toType ();
+
+ } else if (typeString == "haxe.io.Bytes" || bytesAbstract) {
+
+ return TPath ( { pack: [ "lime", "net" ], name: "HTTPRequest", sub: "_HTTPRequest_Bytes", params: [ TPType (paramType.toComplexType ()) ] } ).toType ();
+
+ } else {
+
+ var typeParamString = typeString;
+
+ if (typeArgs.length > 0) {
+
+ typeParamString += "<";
+
+ for (i in 0...typeArgs.length) {
+
+ if (i > 0) typeParamString += ",";
+ typeParamString += typeArgs[i].toString ();
+
+ }
+
+ typeParamString += ">";
+
+ }
+
+ var flattenedTypeString = typeParamString;
+
+ flattenedTypeString = StringTools.replace (flattenedTypeString, "->", "_");
+ flattenedTypeString = StringTools.replace (flattenedTypeString, ".", "_");
+ flattenedTypeString = StringTools.replace (flattenedTypeString, "<", "_");
+ flattenedTypeString = StringTools.replace (flattenedTypeString, ">", "_");
+
+ var name = "_HTTPRequest_" + flattenedTypeString;
+
+ try {
+
+ Context.getType ("lime.net." + name);
+
+ } catch (e:Dynamic) {
+
+ var pos = Context.currentPos ();
+
+ var fields = [
+ { name: "new", access: [ APublic ], kind: FFun({ args: [ { name: "uri", type: macro :String, opt: true } ], expr: macro { super (uri); }, params: [], ret: macro :Void }), pos: Context.currentPos () },
+ { name: "fromBytes", access: [ APrivate, AOverride ], kind: FFun ( { args: [ { name: "bytes", type: macro :haxe.io.Bytes } ], expr: Context.parse ("return " + typeString + ".fromBytes (bytes)", pos), params: [], ret: paramType.toComplexType () } ), pos: pos }
+ ];
+
+ Context.defineType ({
+
+ name: name,
+ pack: [ "lime", "net" ],
+ kind: TDClass ({ pack: [ "lime", "net" ], name: "HTTPRequest", sub: "_HTTPRequest_Bytes", params: [ TPType (paramType.toComplexType ()) ] }, null, false),
+ fields: fields,
+ pos: pos
+
+ });
+
+ }
+
+ return TPath ( { pack: [ "lime", "net" ], name: name, params: [] } ).toType ();
+
+ }
+
+ }
+
+
+ private static function isBytesAbstract (type:AbstractType):Bool {
+
+ while (type != null) {
+
+ switch (type.type) {
+
+ case TInst (t, _):
+
+ return t.get ().module == "haxe.io.Bytes";
+
+ case TAbstract (t, _):
+
+ type = t.get ();
+
+ default:
+
+ return false;
+
+ }
+
+ }
+
+ return false;
+
+ }
+
+
+ private static function isStringAbstract (type:AbstractType):Bool {
+
+ while (type != null) {
+
+ switch (type.type) {
+
+ case TInst (t, _):
+
+ return t.get ().module == "String";
+
+ case TAbstract (t, _):
+
+ type = t.get ();
+
+ default:
+
+ return false;
+
+ }
+
+ }
+
+ return false;
+
+ }
+
+
+}
+
+
+#end
\ No newline at end of file
diff --git a/lime/app/Event.hx b/lime/app/Event.hx
index be7b4c0ec..b7121d967 100644
--- a/lime/app/Event.hx
+++ b/lime/app/Event.hx
@@ -9,7 +9,7 @@ using haxe.macro.Tools;
#end
#if (!macro && !display)
-@:genericBuild(lime.app.Event.build())
+@:genericBuild(lime._macros.EventMacro.build())
#end
diff --git a/lime/app/Preloader.hx b/lime/app/Preloader.hx
index eb305d9e9..d83a209f1 100644
--- a/lime/app/Preloader.hx
+++ b/lime/app/Preloader.hx
@@ -4,8 +4,10 @@ package lime.app;
import haxe.io.Bytes;
import haxe.io.Path;
import lime.app.Event;
-import lime.Assets;
import lime.audio.AudioBuffer;
+import lime.utils.AssetLibrary;
+import lime.utils.Assets;
+import lime.utils.AssetType;
#if (js && html5)
import js.html.Image;
@@ -27,13 +29,9 @@ class Preloader #if flash extends Sprite #end {
public var onComplete = new EventVoid> ();
public var onProgress = new EventInt->Void> ();
- #if (js && html5)
- public static var audioBuffers = new Map ();
- public static var images = new Map ();
- public static var loaders = new Map> ();
- private var loaded = 0;
- private var total = 0;
- #end
+ private var libraries:Array;
+ private var loadedLibraries:Int;
+ private var loadedStage:Bool;
public function new () {
@@ -42,11 +40,20 @@ class Preloader #if flash extends Sprite #end {
super ();
#end
+ libraries = new Array ();
+
onProgress.add (update);
}
+ public function addLibrary (library:AssetLibrary):Void {
+
+ libraries.push (library);
+
+ }
+
+
public function create (config:Config):Void {
#if flash
@@ -58,216 +65,28 @@ class Preloader #if flash extends Sprite #end {
Lib.current.addEventListener (flash.events.Event.ENTER_FRAME, current_onEnter);
#end
- #if (!flash && !html5)
- start ();
- #end
-
}
- public function load (urls:Array, types:Array):Void {
+ public function load ():Void {
- #if (js && html5)
+ loadedLibraries = -1;
- var url = null;
- var cacheVersion = Assets.cache.version;
- var soundPaths = new Map> ();
-
- for (i in 0...urls.length) {
+ for (library in libraries) {
- url = urls[i];
-
- switch (types[i]) {
+ library.load ().onComplete (function (_) {
- case IMAGE:
-
- if (!images.exists (url)) {
-
- var image = new Image ();
- images.set (url, image);
- image.onload = image_onLoad;
- image.src = url + "?" + cacheVersion;
- total++;
-
- }
-
- case BINARY:
-
- if (!loaders.exists (url)) {
-
- var loader = new HTTPRequest ();
- loaders.set (url, loader);
- total++;
-
- }
-
- case TEXT:
-
- if (!loaders.exists (url)) {
-
- var loader = new HTTPRequest ();
- loaders.set (url, loader);
- total++;
-
- }
-
- case MUSIC, SOUND:
-
- var soundName = Path.withoutExtension (url);
- var extension = Path.extension (url);
-
- if (!soundPaths.exists (soundName)) {
-
- soundPaths.set (soundName, []);
- total++;
-
- }
-
- if (extension == "wav") {
-
- soundPaths.get (soundName).push (url);
-
- } else {
-
- soundPaths.get (soundName).unshift (url);
-
- }
-
- case FONT:
-
- total++;
- loadFont (url);
-
- default:
-
- }
-
- }
-
- for (url in loaders.keys ()) {
-
- var loader = loaders.get (url);
- var future = loader.load (url + "?" + cacheVersion);
- future.onComplete (loader_onComplete);
-
- }
-
- for (paths in soundPaths) {
-
- AudioBuffer.loadFromFiles (paths).onComplete (function (audioBuffer) {
-
- for (path in paths) {
-
- audioBuffers.set (path, audioBuffer);
-
- }
-
- audioBuffer_onLoad ();
-
- }).onError (audioBuffer_onLoad);
-
- }
-
- if (total == 0) {
-
- start ();
-
- }
-
- #end
-
- }
-
-
- #if (js && html5)
- private function loadFont (font:String):Void {
-
- if (untyped (Browser.document).fonts && untyped (Browser.document).fonts.load) {
-
- untyped (Browser.document).fonts.load ("1em '" + font + "'").then (function (_) {
-
- loaded ++;
- onProgress.dispatch (loaded, total);
-
- if (loaded == total) {
-
- start ();
-
- }
+ loadedLibraries++;
+ updateProgress ();
});
- } else {
-
- var node:SpanElement = cast Browser.document.createElement ("span");
- node.innerHTML = "giItT1WQy@!-/#";
- var style = node.style;
- style.position = "absolute";
- style.left = "-10000px";
- style.top = "-10000px";
- style.fontSize = "300px";
- style.fontFamily = "sans-serif";
- style.fontVariant = "normal";
- style.fontStyle = "normal";
- style.fontWeight = "normal";
- style.letterSpacing = "0";
- Browser.document.body.appendChild (node);
-
- var width = node.offsetWidth;
- style.fontFamily = "'" + font + "', sans-serif";
-
- var interval:Null = null;
- var found = false;
-
- var checkFont = function () {
-
- if (node.offsetWidth != width) {
-
- // Test font was still not available yet, try waiting one more interval?
- if (!found) {
-
- found = true;
- return false;
-
- }
-
- loaded ++;
-
- if (interval != null) {
-
- Browser.window.clearInterval (interval);
-
- }
-
- node.parentNode.removeChild (node);
- node = null;
-
- onProgress.dispatch (loaded, total);
-
- if (loaded == total) {
-
- start ();
-
- }
-
- return true;
-
- }
-
- return false;
-
- }
-
- if (!checkFont ()) {
-
- interval = Browser.window.setInterval (checkFont, 50);
-
- }
-
}
+ loadedLibraries++;
+ updateProgress ();
+
}
- #end
private function start ():Void {
@@ -294,21 +113,11 @@ class Preloader #if flash extends Sprite #end {
}
-
-
- // Event Handlers
-
-
-
-
- #if (js && html5)
- private function audioBuffer_onLoad (?_):Void {
+ private function updateProgress ():Void {
- loaded++;
+ update (loadedLibraries, libraries.length);
- onProgress.dispatch (loaded, total);
-
- if (loaded == total) {
+ if (#if flash loadedStage && #end loadedLibraries == libraries.length) {
start ();
@@ -317,55 +126,24 @@ class Preloader #if flash extends Sprite #end {
}
- private function image_onLoad (_):Void {
-
- loaded++;
-
- onProgress.dispatch (loaded, total);
-
- if (loaded == total) {
-
- start ();
-
- }
-
- }
-
-
- private function loader_onComplete (_):Void {
-
- loaded++;
-
- onProgress.dispatch (loaded, total);
-
- if (loaded == total) {
-
- start ();
-
- }
-
- }
- #end
-
-
#if flash
private function current_onEnter (event:flash.events.Event):Void {
- if (!complete && Lib.current.loaderInfo.bytesLoaded == Lib.current.loaderInfo.bytesTotal) {
+ if (!loadedStage && Lib.current.loaderInfo.bytesLoaded == Lib.current.loaderInfo.bytesTotal) {
- complete = true;
+ loadedStage = true;
onProgress.dispatch (Lib.current.loaderInfo.bytesLoaded, Lib.current.loaderInfo.bytesTotal);
}
- if (complete) {
+ if (loadedStage) {
Lib.current.removeEventListener (flash.events.Event.ENTER_FRAME, current_onEnter);
Lib.current.loaderInfo.removeEventListener (flash.events.Event.COMPLETE, loaderInfo_onComplete);
Lib.current.loaderInfo.removeEventListener (flash.events.Event.INIT, loaderInfo_onInit);
Lib.current.loaderInfo.removeEventListener (ProgressEvent.PROGRESS, loaderInfo_onProgress);
- start ();
+ updateProgress ();
}
@@ -374,7 +152,7 @@ class Preloader #if flash extends Sprite #end {
private function loaderInfo_onComplete (event:flash.events.Event):Void {
- complete = true;
+ loadedStage = true;
onProgress.dispatch (Lib.current.loaderInfo.bytesLoaded, Lib.current.loaderInfo.bytesTotal);
}
diff --git a/lime/graphics/Image.hx b/lime/graphics/Image.hx
index 8db11f888..606e07921 100644
--- a/lime/graphics/Image.hx
+++ b/lime/graphics/Image.hx
@@ -7,6 +7,8 @@ import haxe.io.BytesData;
import haxe.io.BytesInput;
import haxe.io.BytesOutput;
import lime.app.Application;
+import lime.app.Future;
+import lime.app.Promise;
import lime.graphics.format.BMP;
import lime.graphics.format.JPEG;
import lime.graphics.format.PNG;
@@ -52,7 +54,7 @@ import lime.graphics.console.TextureData;
@:build(lime.system.CFFI.build())
#end
-@:autoBuild(lime.Assets.embedImage())
+@:autoBuild(lime._macros.AssetsMacro.embedImage())
@:allow(lime.graphics.util.ImageCanvasUtil)
@:allow(lime.graphics.util.ImageDataUtil)
@:access(lime.app.Application)
@@ -465,7 +467,7 @@ class Image {
}
- public static function fromBase64 (base64:String, type:String, onload:Image -> Void):Image {
+ public static function fromBase64 (base64:String, type:String, onload:Image->Void):Image {
if (base64 == null) return null;
var image = new Image ();
@@ -493,7 +495,7 @@ class Image {
}
- public static function fromBytes (bytes:Bytes, onload:Image -> Void = null):Image {
+ public static function fromBytes (bytes:Bytes, onload:Image->Void = null):Image {
if (bytes == null) return null;
var image = new Image ();
@@ -726,6 +728,77 @@ class Image {
}
+ public static function loadFromBase64 (base64:String, type:String):Future {
+
+ var promise = new Promise ();
+
+ // TODO: Handle error, progress
+
+ fromBase64 (base64, type, function (image) {
+
+ promise.complete (image);
+
+ });
+
+ return promise.future;
+
+ }
+
+
+ public static function loadFromBytes (bytes:Bytes):Future {
+
+ #if (js && html5)
+
+ var promise = new Promise ();
+
+ // TODO: Handle error, progress
+
+ fromBytes (bytes, function (image) {
+
+ promise.complete (image);
+
+ });
+
+ return promise.future;
+
+ #else
+
+ return new Future (function () return fromBytes (bytes), true);
+
+ #end
+
+ }
+
+
+ public static function loadFromFile (path:String):Future {
+
+ #if (js && html5)
+
+ var promise = new Promise ();
+
+ // TODO: Handle progress
+
+ fromFile (path, function (image) {
+
+ promise.complete (image);
+
+ }, function () {
+
+ promise.error ("");
+
+ });
+
+ return promise.future;
+
+ #else
+
+ return new Future (function () return fromFile (path), true);
+
+ #end
+
+ }
+
+
public function merge (sourceImage:Image, sourceRect:Rectangle, destPoint:Vector2, redMultiplier:Int, greenMultiplier:Int, blueMultiplier:Int, alphaMultiplier:Int):Void {
if (buffer == null || sourceImage == null) return;
diff --git a/lime/net/HTTPRequest.hx b/lime/net/HTTPRequest.hx
index 48150625d..c8c30324e 100644
--- a/lime/net/HTTPRequest.hx
+++ b/lime/net/HTTPRequest.hx
@@ -1,4 +1,4 @@
-package lime.net; #if !macro
+package lime.net;
import haxe.io.Bytes;
@@ -13,7 +13,7 @@ class HTTPRequest {
#else
-@:genericBuild(lime.net.HTTPRequest.build())
+@:genericBuild(lime._macros.HTTPRequestMacro.build())
class HTTPRequest extends AbstractHTTPRequest {}
private class AbstractHTTPRequest implements _IHTTPRequest {
@@ -193,219 +193,4 @@ private typedef HTTPRequestBackend = lime._backend.html5.HTML5HTTPRequest;
#else
private typedef HTTPRequestBackend = lime._backend.native.NativeHTTPRequest;
#end
-#end
-
-
-#else
-
-
-import haxe.macro.Context;
-import haxe.macro.Expr;
-import haxe.macro.Type;
-using haxe.macro.Tools;
-
-
-class HTTPRequest {
-
-
- private static function build () {
-
- var paramType;
- var type:BaseType, typeArgs;
- var stringAbstract = false;
- var bytesAbstract = false;
-
- switch (Context.follow (Context.getLocalType ())) {
-
- case TInst (localType, [ t ]):
-
- paramType = t;
-
- switch (t) {
-
- case TInst (t, args):
-
- type = t.get ();
- typeArgs = args;
-
- case TAbstract (t, args):
-
- type = t.get ();
- typeArgs = args;
-
- stringAbstract = isStringAbstract (t.get ());
- if (!stringAbstract) bytesAbstract = isBytesAbstract (t.get ());
-
- case TType (t, args):
-
- type = t.get ();
- typeArgs = args;
-
- case TMono (_):
-
- Context.fatalError ("Invalid number of type parameters for " + localType.toString (), Context.currentPos ());
- return null;
-
- case TDynamic (_):
-
- switch (Context.getType ("haxe.io.Bytes")) {
-
- case TInst (t, args):
-
- type = t.get ();
- typeArgs = args;
-
- default:
-
- throw false;
-
- }
-
- default:
-
- throw false;
-
- }
-
- default:
-
- throw false;
-
- }
-
- var typeString = type.module;
-
- if (type.name != type.module && !StringTools.endsWith (type.module, "." + type.name)) {
-
- typeString += "." + type.name;
-
- }
-
- if (typeString == "String" || stringAbstract) {
-
- return TPath ( { pack: [ "lime", "net" ], name: "HTTPRequest", sub: "_HTTPRequest_String", params: [ TPType (paramType.toComplexType ()) ] } ).toType ();
-
- } else if (typeString == "haxe.io.Bytes" || bytesAbstract) {
-
- return TPath ( { pack: [ "lime", "net" ], name: "HTTPRequest", sub: "_HTTPRequest_Bytes", params: [ TPType (paramType.toComplexType ()) ] } ).toType ();
-
- } else {
-
- var typeParamString = typeString;
-
- if (typeArgs.length > 0) {
-
- typeParamString += "<";
-
- for (i in 0...typeArgs.length) {
-
- if (i > 0) typeParamString += ",";
- typeParamString += typeArgs[i].toString ();
-
- }
-
- typeParamString += ">";
-
- }
-
- var flattenedTypeString = typeParamString;
-
- flattenedTypeString = StringTools.replace (flattenedTypeString, "->", "_");
- flattenedTypeString = StringTools.replace (flattenedTypeString, ".", "_");
- flattenedTypeString = StringTools.replace (flattenedTypeString, "<", "_");
- flattenedTypeString = StringTools.replace (flattenedTypeString, ">", "_");
-
- var name = "_HTTPRequest_" + flattenedTypeString;
-
- try {
-
- Context.getType ("lime.net." + name);
-
- } catch (e:Dynamic) {
-
- var pos = Context.currentPos ();
-
- var fields = [
- { name: "new", access: [ APublic ], kind: FFun({ args: [ { name: "uri", type: macro :String, opt: true } ], expr: macro { super (uri); }, params: [], ret: macro :Void }), pos: Context.currentPos () },
- { name: "fromBytes", access: [ APrivate, AOverride ], kind: FFun ( { args: [ { name: "bytes", type: macro :haxe.io.Bytes } ], expr: Context.parse ("return " + typeString + ".fromBytes (bytes)", pos), params: [], ret: paramType.toComplexType () } ), pos: pos }
- ];
-
- Context.defineType ({
-
- name: name,
- pack: [ "lime", "net" ],
- kind: TDClass ({ pack: [ "lime", "net" ], name: "HTTPRequest", sub: "_HTTPRequest_Bytes", params: [ TPType (paramType.toComplexType ()) ] }, null, false),
- fields: fields,
- pos: pos
-
- });
-
- }
-
- return TPath ( { pack: [ "lime", "net" ], name: name, params: [] } ).toType ();
-
- }
-
- }
-
-
- private static function isBytesAbstract (type:AbstractType):Bool {
-
- while (type != null) {
-
- switch (type.type) {
-
- case TInst (t, _):
-
- return t.get ().module == "haxe.io.Bytes";
-
- case TAbstract (t, _):
-
- type = t.get ();
-
- default:
-
- return false;
-
- }
-
- }
-
- return false;
-
- }
-
-
- private static function isStringAbstract (type:AbstractType):Bool {
-
- while (type != null) {
-
- switch (type.type) {
-
- case TInst (t, _):
-
- return t.get ().module == "String";
-
- case TAbstract (t, _):
-
- type = t.get ();
-
- default:
-
- return false;
-
- }
-
- }
-
- return false;
-
- }
-
-
-
-
-}
-
-
#end
\ No newline at end of file
diff --git a/lime/system/CFFI.hx b/lime/system/CFFI.hx
index 0b8539163..fe6a5015c 100644
--- a/lime/system/CFFI.hx
+++ b/lime/system/CFFI.hx
@@ -1,6 +1,8 @@
-package lime.system; #if !macro
+package lime.system;
+import lime._macros.CFFIMacro;
+
#if sys
import sys.io.Process;
#end
@@ -34,6 +36,15 @@ class CFFI {
}
+ #if macro
+ public static function build (defaultLibrary:String = "lime") {
+
+ return CFFIMacro.build (defaultLibrary);
+
+ }
+ #end
+
+
/**
* Tries to load a native CFFI primitive on compatible platforms
* @param library The name of the native library (such as "lime")
@@ -181,7 +192,7 @@ class CFFI {
public static macro function loadPrime (library:String, method:String, signature:String, lazy:Bool = false):Dynamic {
- #if !display
+ #if (!display && !macro)
return cpp.Prime.load (library, method, signature, lazy);
#else
var args = signature.length - 1;
@@ -437,343 +448,4 @@ class CFFI {
}
}
-#end
-
-
-#else
-
-
-import haxe.macro.Context;
-import haxe.macro.Expr;
-import haxe.macro.Type;
-
-using haxe.macro.ComplexTypeTools;
-using haxe.macro.ExprTools;
-using haxe.macro.TypeTools;
-
-
-class CFFI {
-
-
- public static function build (defaultLibrary:String = "lime"):Array {
-
- var pos = Context.currentPos ();
- var fields = Context.getBuildFields ();
- var newFields:Array = [];
-
- for (field in fields) {
-
- switch (field) {
-
- case _ => { kind: FFun (fun), meta: meta } :
-
- for (m in meta) {
-
- if (m.name == ":cffi") {
-
- var library = null;
- var method = null;
- var lazy = false;
-
- if (Reflect.hasField (m, "params")) {
-
- if (m.params.length > 0) library = m.params[0].getValue ();
- if (m.params.length > 1) method = m.params[1].getValue ();
- if (m.params.length > 2) lazy = m.params[2].getValue ();
-
- }
-
- if (library == null || library == "") {
-
- library = defaultLibrary;
-
- }
-
- if (method == null || method == "") {
-
- method = field.name;
-
- }
-
- var typeArgs = [];
-
- for (arg in fun.args) {
-
- typeArgs.push ( { name: arg.name, opt: false, t: arg.type.toType () } );
-
- }
-
- var type = __getFunctionType (typeArgs, fun.ret.toType ());
-
- var typeString = type.string;
- var typeSignature = type.signature;
- var expr = "";
-
- if (Context.defined ("display") || Context.defined ("disable_cffi")) {
-
- switch (type.result.toString ()) {
-
- case "Int", "Float":
-
- expr += "return 0";
-
- case "Bool":
-
- expr += "return false";
-
- default:
-
- expr += "return null";
-
- }
-
- } else {
-
- var cffiName = "cffi_" + field.name;
- var cffiExpr, cffiType;
-
- if (Context.defined ("cpp")) {
-
- cffiExpr = 'new cpp.Callable<$typeString> (cpp.Prime._loadPrime ("$library", "$method", "$typeSignature", $lazy))';
-
- } else {
-
- var args = typeSignature.length - 1;
-
- if (args > 5) {
-
- args = -1;
-
- }
-
- cffiExpr = 'new cpp.Callable<$typeString> (lime.system.CFFI.load ("$library", "$method", $args, $lazy))';
-
- }
-
- cffiType = TPath ( { pack: [ "cpp" ], name: "Callable", params: [ TPType (TFun (type.args, type.result).toComplexType ()) ] } );
-
- newFields.push ( { name: cffiName, access: [ APrivate, AStatic ], kind: FieldType.FVar (cffiType, Context.parse (cffiExpr, field.pos)), pos: field.pos } );
-
- if (type.result.toString () != "Void" && type.result.toString () != "cpp.Void") {
-
- expr += "return ";
-
- }
-
- expr += '$cffiName.call (';
-
- for (i in 0...type.args.length) {
-
- if (i > 0) expr += ", ";
- expr += type.args[i].name;
-
- }
-
- expr += ")";
-
- }
-
- field.access.push (AInline);
- fun.expr = Context.parse (expr, field.pos);
-
- }
-
- }
-
- default:
-
- }
-
- }
-
- fields = fields.concat (newFields);
- return fields;
-
- }
-
-
- private static function __getFunctionType (args:Array<{ name : String, opt : Bool, t : Type }>, result:Type) {
-
- #if (!disable_cffi && !display)
- var useCPPTypes = Context.defined ("cpp");
- #else
- var useCPPTypes = false;
- #end
-
- var typeArgs = [];
- var typeResult = null;
- var typeSignature = "";
-
- for (arg in args) {
-
- switch (arg.t.toString ()) {
-
- case "Int", "cpp.Int16", "cpp.Int32":
-
- typeArgs.push (arg);
- typeSignature += "i";
-
- case "Bool":
-
- typeArgs.push (arg);
- typeSignature += "b";
-
- case "cpp.Float32":
-
- if (useCPPTypes) {
-
- typeArgs.push ( { name: arg.name, opt: false, t: (macro :cpp.Float32).toType () } );
-
- } else {
-
- typeArgs.push (arg);
-
- }
-
- typeSignature += "f";
-
- case "Float", "cpp.Float64":
-
- typeArgs.push (arg);
- typeSignature += "d";
-
- case "String":
-
- typeArgs.push (arg);
- typeSignature += "s";
-
- case "cpp.ConstCharStar":
-
- typeArgs.push (arg);
- typeSignature += "c";
-
- case "Void", "cpp.Void":
-
- if (useCPPTypes) {
-
- typeArgs.push ( { name: arg.name, opt: false, t: (macro :cpp.Void).toType () } );
-
- } else {
-
- typeArgs.push (arg);
-
- }
-
- typeSignature += "v";
-
- default:
-
- if (useCPPTypes) {
-
- typeArgs.push ( { name: arg.name, opt: false, t: (macro :cpp.Object).toType () } );
-
- } else {
-
- typeArgs.push ( { name: arg.name, opt: false, t: (macro :Dynamic).toType () } );
-
- }
-
- typeSignature += "o";
-
- }
-
- }
-
- switch (result.toString ()) {
-
- case "Int", "cpp.Int16", "cpp.Int32":
-
- typeResult = result;
- typeSignature += "i";
-
- case "Bool":
-
- typeResult = result;
- typeSignature += "b";
-
- case "cpp.Float32":
-
- if (useCPPTypes) {
-
- typeResult = (macro :cpp.Float32).toType ();
-
- } else {
-
- typeResult = result;
-
- }
-
- typeSignature += "f";
-
- case "Float", "cpp.Float64":
-
- typeResult = result;
- typeSignature += "d";
-
- case "String":
-
- typeResult = result;
- typeSignature += "s";
-
- case "cpp.ConstCharStar":
-
- typeResult = result;
- typeSignature += "c";
-
- case "Void", "cpp.Void":
-
- if (useCPPTypes) {
-
- typeResult = (macro :cpp.Void).toType ();
-
- } else {
-
- typeResult = result;
-
- }
-
- typeSignature += "v";
-
- default:
-
- if (useCPPTypes) {
-
- typeResult = (macro :cpp.Object).toType ();
-
- } else {
-
- typeResult = (macro :Dynamic).toType ();
-
- }
-
- typeSignature += "o";
-
- }
-
- var typeString = "";
-
- if (typeArgs.length == 0) {
-
- typeString = "Void->";
-
- } else {
-
- for (arg in typeArgs) {
-
- typeString += arg.t.toString () + "->";
-
- }
-
- }
-
- typeString += typeResult.toString ();
-
- return { args: typeArgs, result: typeResult, string: typeString, signature: typeSignature };
-
- }
-
-
-}
-
-
#end
\ No newline at end of file
diff --git a/lime/system/System.hx b/lime/system/System.hx
index 1ab456945..4bc1d0dce 100644
--- a/lime/system/System.hx
+++ b/lime/system/System.hx
@@ -128,7 +128,7 @@ class System {
#if flash
display.dpi = Capabilities.screenDPI;
display.currentMode = new DisplayMode (Std.int (Capabilities.screenResolutionX), Std.int (Capabilities.screenResolutionY), 60, ARGB32);
- #else
+ #elseif (js && html5)
display.dpi = 96; // TODO: Detect DPI on HTML5
display.currentMode = new DisplayMode (Browser.window.screen.width, Browser.window.screen.height, 60, ARGB32);
#end
diff --git a/lime/text/Font.hx b/lime/text/Font.hx
index 9d5a324b6..b8362af98 100644
--- a/lime/text/Font.hx
+++ b/lime/text/Font.hx
@@ -2,6 +2,8 @@ package lime.text;
import haxe.io.Bytes;
+import lime.app.Future;
+import lime.app.Promise;
import lime.graphics.Image;
import lime.graphics.ImageBuffer;
import lime.math.Vector2;
@@ -11,6 +13,8 @@ import lime.utils.UInt8Array;
#if (js && html5)
import js.html.CanvasElement;
import js.html.CanvasRenderingContext2D;
+import js.html.SpanElement;
+import js.Browser;
#end
#if (lime_cffi && !macro)
@@ -22,7 +26,7 @@ import haxe.io.Path;
#end
#if (!display && !nodejs && !macro)
-@:autoBuild(lime.Assets.embedFont())
+@:autoBuild(lime._macros.AssetsMacro.embedFont())
#end
@:access(lime.text.Glyph)
@@ -113,6 +117,36 @@ class Font {
}
+ public static function loadFromBytes (bytes:Bytes):Future {
+
+ return Future.withValue (fromBytes (bytes));
+
+ }
+
+
+ public static function loadFromFile (path:String):Future {
+
+ return Future.withValue (fromFile (path));
+
+ }
+
+
+ public static function loadFromName (path:String):Future {
+
+ #if (js && html5)
+
+ var font = new Font ();
+ return font.__loadFromName (path);
+
+ #else
+
+ return cast Future.withError ("");
+
+ #end
+
+ }
+
+
public function getGlyph (character:String):Glyph {
#if (lime_cffi && !macro)
@@ -399,6 +433,94 @@ class Font {
}
+ private function __loadFromName (name:String):Future {
+
+ var promise = new Promise ();
+
+ #if (js && html5)
+
+ this.name = name;
+ var font = name;
+
+ if (untyped (Browser.document).fonts && untyped (Browser.document).fonts.load) {
+
+ untyped (Browser.document).fonts.load ("1em '" + font + "'").then (function (_) {
+
+ promise.complete (this);
+
+ });
+
+ } else {
+
+ var node:SpanElement = cast Browser.document.createElement ("span");
+ node.innerHTML = "giItT1WQy@!-/#";
+ var style = node.style;
+ style.position = "absolute";
+ style.left = "-10000px";
+ style.top = "-10000px";
+ style.fontSize = "300px";
+ style.fontFamily = "sans-serif";
+ style.fontVariant = "normal";
+ style.fontStyle = "normal";
+ style.fontWeight = "normal";
+ style.letterSpacing = "0";
+ Browser.document.body.appendChild (node);
+
+ var width = node.offsetWidth;
+ style.fontFamily = "'" + font + "', sans-serif";
+
+ var interval:Null = null;
+ var found = false;
+
+ var checkFont = function () {
+
+ if (node.offsetWidth != width) {
+
+ // Test font was still not available yet, try waiting one more interval?
+ if (!found) {
+
+ found = true;
+ return false;
+
+ }
+
+ if (interval != null) {
+
+ Browser.window.clearInterval (interval);
+
+ }
+
+ node.parentNode.removeChild (node);
+ node = null;
+
+ promise.complete (this);
+ return true;
+
+ }
+
+ return false;
+
+ }
+
+ if (!checkFont ()) {
+
+ interval = Browser.window.setInterval (checkFont, 50);
+
+ }
+
+ }
+
+ #else
+
+ promise.error ("");
+
+ #end
+
+ return promise.future;
+
+ }
+
+
@:noCompletion private function __setSize (size:Int):Void {
#if (lime_cffi && !macro)
diff --git a/lime/utils/AssetCache.hx b/lime/utils/AssetCache.hx
new file mode 100644
index 000000000..04d1b6c44
--- /dev/null
+++ b/lime/utils/AssetCache.hx
@@ -0,0 +1,86 @@
+package lime.utils;
+
+
+import lime.audio.AudioBuffer;
+import lime.graphics.Image;
+
+
+class AssetCache {
+
+
+ public var audio:Map;
+ public var enabled:Bool = true;
+ public var image:Map;
+ public var font:Map;
+ public var version:Int;
+
+
+ public function new () {
+
+ audio = new Map ();
+ font = new Map ();
+ image = new Map ();
+ version = AssetCache.cacheVersion ();
+
+ }
+
+
+ public static macro function cacheVersion () {
+
+ return macro $v{Std.int (Math.random () * 1000000)};
+
+ }
+
+
+ public function clear (prefix:String = null):Void {
+
+ if (prefix == null) {
+
+ audio = new Map ();
+ font = new Map ();
+ image = new Map ();
+
+ } else {
+
+ var keys = audio.keys ();
+
+ for (key in keys) {
+
+ if (StringTools.startsWith (key, prefix)) {
+
+ audio.remove (key);
+
+ }
+
+ }
+
+ var keys = font.keys ();
+
+ for (key in keys) {
+
+ if (StringTools.startsWith (key, prefix)) {
+
+ font.remove (key);
+
+ }
+
+ }
+
+ var keys = image.keys ();
+
+ for (key in keys) {
+
+ if (StringTools.startsWith (key, prefix)) {
+
+ image.remove (key);
+
+ }
+
+ }
+
+ }
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/lime/utils/AssetLibrary.hx b/lime/utils/AssetLibrary.hx
new file mode 100644
index 000000000..a6376c33b
--- /dev/null
+++ b/lime/utils/AssetLibrary.hx
@@ -0,0 +1,596 @@
+package lime.utils;
+
+
+import lime.app.Event;
+import lime.app.Future;
+import lime.app.Promise;
+import lime.audio.AudioBuffer;
+import lime.graphics.Image;
+import lime.text.Font;
+
+#if flash
+import flash.display.BitmapData;
+import flash.media.Sound;
+#end
+
+
+class AssetLibrary {
+
+
+ public var onChange = new EventVoid> ();
+
+ private var cachedAudioBuffers = new Map ();
+ private var cachedBytes = new Map ();
+ private var cachedFonts = new Map ();
+ private var cachedImages = new Map ();
+ private var cachedText = new Map ();
+ private var classTypes = new Map> ();
+ private var paths = new Map ();
+ private var preload = new Map ();
+ private var progressBytesLoadedCache:Map;
+ private var progressBytesLoaded:Int;
+ private var progressBytesTotal:Int;
+ private var progressLoaded:Int;
+ private var progressTotal:Int;
+ private var promise:Promise;
+ private var types = new Map ();
+
+
+ public function new () {
+
+
+
+ }
+
+
+ public function exists (id:String, type:String):Bool {
+
+ var requestedType = type != null ? cast (type, AssetType) : null;
+ var assetType = types.get (id);
+
+ if (assetType != null) {
+
+ if (assetType == requestedType || ((requestedType == SOUND || requestedType == MUSIC) && (assetType == MUSIC || assetType == SOUND))) {
+
+ return true;
+
+ }
+
+ #if flash
+
+ if (requestedType == BINARY && (assetType == BINARY || assetType == TEXT || assetType == IMAGE)) {
+
+ return true;
+
+ } else if (requestedType == TEXT && assetType == BINARY) {
+
+ return true;
+
+ } else if (requestedType == null || paths.exists (id)) {
+
+ return true;
+
+ }
+
+ #else
+
+ if (requestedType == BINARY || requestedType == null || (assetType == BINARY && requestedType == TEXT)) {
+
+ return true;
+
+ }
+
+ #end
+
+ }
+
+ return false;
+
+ }
+
+
+ public function getAudioBuffer (id:String):AudioBuffer {
+
+ if (cachedAudioBuffers.exists (id)) {
+
+ return cachedAudioBuffers.get (id);
+
+ } else if (classTypes.exists (id)) {
+
+ #if flash
+
+ var buffer = new AudioBuffer ();
+ buffer.src = cast (Type.createInstance (classTypes.get (id), []), Sound);
+ return buffer;
+
+ #else
+
+ return AudioBuffer.fromBytes (cast (Type.createInstance (classTypes.get (id), []), Bytes));
+
+ #end
+
+ } else {
+
+ return AudioBuffer.fromFile (paths.get (id));
+
+ }
+
+ }
+
+
+ public function getBytes (id:String):Bytes {
+
+ if (cachedBytes.exists (id)) {
+
+ return cachedBytes.get (id);
+
+ } else if (classTypes.exists (id)) {
+
+ #if flash
+
+ switch (types.get (id)) {
+
+ case TEXT, BINARY:
+
+ return Bytes.ofData (cast (Type.createInstance (classTypes.get (id), []), flash.utils.ByteArray));
+
+ case IMAGE:
+
+ var bitmapData = cast (Type.createInstance (classTypes.get (id), []), BitmapData);
+ return Bytes.ofData (bitmapData.getPixels (bitmapData.rect));
+
+ default:
+
+ return null;
+
+ }
+
+ #else
+
+ return cast (Type.createInstance (classTypes.get (id), []), Bytes);
+
+ #end
+
+ } else {
+
+ return Bytes.fromFile (paths.get (id));
+
+ }
+
+ }
+
+
+ public function getFont (id:String):Font {
+
+ if (cachedFonts.exists (id)) {
+
+ return cachedFonts.get (id);
+
+ } else if (classTypes.exists (id)) {
+
+ #if flash
+
+ var src = Type.createInstance (classTypes.get (id), []);
+
+ var font = new Font (src.fontName);
+ font.src = src;
+ return font;
+
+ #else
+
+ return cast (Type.createInstance (classTypes.get (id), []), Font);
+
+ #end
+
+ } else {
+
+ return Font.fromFile (paths.get (id));
+
+ }
+
+ }
+
+
+ public function getImage (id:String):Image {
+
+ if (cachedImages.exists (id)) {
+
+ return cachedImages.get (id);
+
+ } else if (classTypes.exists (id)) {
+
+ #if flash
+
+ return Image.fromBitmapData (cast (Type.createInstance (classTypes.get (id), []), BitmapData));
+
+ #else
+
+ return cast (Type.createInstance (classTypes.get (id), []), Image);
+
+ #end
+
+ } else {
+
+ return Image.fromFile (paths.get (id));
+
+ }
+
+ }
+
+
+ public function getPath (id:String):String {
+
+ return paths.get (id);
+
+ }
+
+
+ public function getText (id:String):String {
+
+ if (cachedText.exists (id)) {
+
+ return cachedText.get (id);
+
+ } else {
+
+ var bytes = getBytes (id);
+
+ if (bytes == null) {
+
+ return null;
+
+ } else {
+
+ return bytes.getString (0, bytes.length);
+
+ }
+
+ }
+
+ }
+
+
+ public function isLocal (id:String, type:String):Bool {
+
+ #if sys
+
+ return true;
+
+ #else
+
+ if (classTypes.exists (id)) {
+
+ return true;
+
+ }
+
+ var requestedType = type != null ? cast (type, AssetType) : null;
+
+ return switch (requestedType) {
+
+ case IMAGE:
+
+ cachedImages.exists (id);
+
+ case MUSIC, SOUND:
+
+ cachedAudioBuffers.exists (id);
+
+ default:
+
+ cachedBytes.exists (id) || cachedText.exists (id);
+
+ }
+
+ #end
+
+ }
+
+
+ public function list (type:String):Array {
+
+ var requestedType = type != null ? cast (type, AssetType) : null;
+ var items = [];
+
+ for (id in types.keys ()) {
+
+ if (requestedType == null || exists (id, type)) {
+
+ items.push (id);
+
+ }
+
+ }
+
+ return items;
+
+ }
+
+
+ public function load ():Future {
+
+ if (promise == null) {
+
+ promise = new Promise ();
+ progressBytesLoadedCache = new Map ();
+
+ progressLoaded = 0;
+ progressTotal = 1;
+
+ for (id in preload.keys ()) {
+
+ trace (id, types.get (id));
+
+ switch (types.get (id)) {
+
+ case BINARY:
+
+ progressTotal++;
+
+ var future = loadBytes (id);
+ future.onProgress (load_onProgress.bind (id));
+ future.onError (load_onError.bind (id));
+ future.onComplete (loadBytes_onComplete.bind (id));
+
+ case FONT:
+
+ progressTotal++;
+
+ var future = loadFont (id);
+ future.onProgress (load_onProgress.bind (id));
+ future.onError (load_onError.bind (id));
+ future.onComplete (loadFont_onComplete.bind (id));
+
+ case IMAGE:
+
+ progressTotal++;
+
+ var future = loadImage (id);
+ future.onProgress (load_onProgress.bind (id));
+ future.onError (load_onError.bind (id));
+ future.onComplete (loadImage_onComplete.bind (id));
+
+ case MUSIC, SOUND:
+
+ progressTotal++;
+
+ var future = loadAudioBuffer (id);
+ future.onProgress (load_onProgress.bind (id));
+ future.onError (load_onError.bind (id));
+ future.onComplete (loadAudioBuffer_onComplete.bind (id));
+
+ case TEXT:
+
+ progressTotal++;
+
+ var future = loadText (id);
+ future.onProgress (load_onProgress.bind (id));
+ future.onError (load_onError.bind (id));
+ future.onComplete (loadText_onComplete.bind (id));
+
+ default:
+
+ }
+
+ }
+
+ updateProgressLoaded ();
+
+ }
+
+ return promise.future;
+
+ }
+
+
+ public function loadAudioBuffer (id:String):Future {
+
+ if (cachedAudioBuffers.exists (id)) {
+
+ return Future.withValue (cachedAudioBuffers.get (id));
+
+ } else if (classTypes.exists (id)) {
+
+ return Future.withValue (Type.createInstance (classTypes.get (id), []));
+
+ } else {
+
+ return AudioBuffer.loadFromFile (paths.get (id));
+
+ }
+
+ }
+
+
+ public function loadBytes (id:String):Future {
+
+ if (cachedBytes.exists (id)) {
+
+ return Future.withValue (cachedBytes.get (id));
+
+ } else if (classTypes.exists (id)) {
+
+ return Future.withValue (Type.createInstance (classTypes.get (id), []));
+
+ } else {
+
+ return Bytes.loadFromFile (paths.get (id));
+
+ }
+
+ }
+
+
+ public function loadFont (id:String):Future {
+
+ if (cachedFonts.exists (id)) {
+
+ return Future.withValue (cachedFonts.get (id));
+
+ } else if (classTypes.exists (id)) {
+
+ return Future.withValue (Type.createInstance (classTypes.get (id), []));
+
+ } else {
+
+ #if (js && html5)
+ return Font.loadFromName (paths.get (id));
+ #else
+ return Font.loadFromFile (paths.get (id));
+ #end
+
+ }
+
+ }
+
+
+ public function loadImage (id:String):Future {
+
+ if (cachedImages.exists (id)) {
+
+ return Future.withValue (cachedImages.get (id));
+
+ } else if (classTypes.exists (id)) {
+
+ return Future.withValue (Type.createInstance (classTypes.get (id), []));
+
+ } else {
+
+ return Image.loadFromFile (paths.get (id));
+
+ }
+
+ }
+
+
+ public function loadText (id:String):Future {
+
+ if (cachedText.exists (id)) {
+
+ return Future.withValue (cachedText.get (id));
+
+ } else {
+
+ return loadBytes (id).then (function (bytes) {
+
+ return new Future (function () {
+
+ if (bytes == null) {
+
+ return null;
+
+ } else {
+
+ return bytes.getString (0, bytes.length);
+
+ }
+
+ }, true);
+
+ });
+
+ }
+
+ }
+
+
+ public function unload ():Void {
+
+
+
+ }
+
+
+ private function updateProgressLoaded ():Void {
+
+ progressLoaded++;
+
+ if (progressLoaded == progressTotal) {
+
+ promise.complete (this);
+
+ }
+
+ }
+
+
+
+
+ // Event Handlers
+
+
+
+
+ private function loadAudioBuffer_onComplete (id:String, audioBuffer:AudioBuffer):Void {
+
+ cachedAudioBuffers.set (id, audioBuffer);
+ updateProgressLoaded ();
+
+ }
+
+
+ private function loadBytes_onComplete (id:String, bytes:Bytes):Void {
+
+ cachedBytes.set (id, bytes);
+ updateProgressLoaded ();
+
+ }
+
+
+ private function loadFont_onComplete (id:String, font:Font):Void {
+
+ cachedFonts.set (id, font);
+ updateProgressLoaded ();
+
+ }
+
+
+ private function loadImage_onComplete (id:String, image:Image):Void {
+
+ cachedImages.set (id, image);
+ updateProgressLoaded ();
+
+ }
+
+
+ private function loadText_onComplete (id:String, text:String):Void {
+
+ cachedText.set (id, text);
+ updateProgressLoaded ();
+
+ }
+
+
+ private function load_onError (id:String, message:Dynamic):Void {
+
+ promise.error ("Error loading asset \"" + id + "\"");
+
+ }
+
+
+ private function load_onProgress (id:String, bytesLoaded:Int, bytesTotal:Int):Void {
+
+ if (progressBytesLoadedCache.exists (id)) {
+
+ var previous = progressBytesLoadedCache.get (id);
+ progressBytesLoaded += (bytesLoaded - previous);
+ progressBytesLoadedCache.set (id, bytesLoaded);
+
+ promise.progress (progressBytesLoaded, progressBytesTotal);
+
+ } else {
+
+ if (bytesTotal > 0) {
+
+ progressBytesLoadedCache.set (id, bytesLoaded);
+ progressBytesLoaded += bytesLoaded;
+ progressBytesTotal += bytesTotal;
+
+ }
+
+ }
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/lime/utils/AssetManifest.hx b/lime/utils/AssetManifest.hx
new file mode 100644
index 000000000..e69de29bb
diff --git a/lime/utils/AssetType.hx b/lime/utils/AssetType.hx
new file mode 100644
index 000000000..1eb965d49
--- /dev/null
+++ b/lime/utils/AssetType.hx
@@ -0,0 +1,14 @@
+package lime.utils;
+
+
+@:enum abstract AssetType(String) {
+
+ var BINARY = "BINARY";
+ var FONT = "FONT";
+ var IMAGE = "IMAGE";
+ var MUSIC = "MUSIC";
+ var SOUND = "SOUND";
+ var TEMPLATE = "TEMPLATE";
+ var TEXT = "TEXT";
+
+}
\ No newline at end of file
diff --git a/lime/utils/Assets.hx b/lime/utils/Assets.hx
new file mode 100644
index 000000000..8db93f456
--- /dev/null
+++ b/lime/utils/Assets.hx
@@ -0,0 +1,831 @@
+package lime.utils;
+
+
+import haxe.CallStack;
+import haxe.Json;
+import haxe.Unserializer;
+import lime.app.Event;
+import lime.app.Promise;
+import lime.app.Future;
+import lime.audio.AudioBuffer;
+import lime.graphics.Image;
+import lime.text.Font;
+import lime.utils.Bytes;
+import lime.utils.Log;
+
+
+/**
+ * The Assets class provides a cross-platform interface to access
+ * embedded images, fonts, sounds and other resource files.
+ *
+ * The contents are populated automatically when an application
+ * is compiled using the Lime command-line tools, based on the
+ * contents of the *.xml project file.
+ *
+ * For most platforms, the assets are included in the same directory
+ * or package as the application, and the paths are handled
+ * automatically. For web content, the assets are preloaded before
+ * the start of the rest of the application. You can customize the
+ * preloader by extending the NMEPreloader class,
+ * and specifying a custom preloader using
+ * in the project file.
+ */
+
+@:access(lime.utils.AssetLibrary)
+
+
+class Assets {
+
+
+ public static var cache:AssetCache = new AssetCache ();
+ public static var libraries (default, null) = new Map ();
+ public static var onChange = new EventVoid> ();
+
+
+ public static function exists (id:String, type:AssetType = null):Bool {
+
+ #if (tools && !display)
+
+ if (type == null) {
+
+ type = BINARY;
+
+ }
+
+ var libraryName = id.substring (0, id.indexOf (":"));
+ var symbolName = id.substr (id.indexOf (":") + 1);
+ var library = getLibrary (libraryName);
+
+ if (library != null) {
+
+ return library.exists (symbolName, cast type);
+
+ }
+
+ #end
+
+ return false;
+
+ }
+
+
+ /**
+ * Gets an instance of an embedded sound
+ * @usage var sound = Assets.getSound("sound.wav");
+ * @param id The ID or asset path for the sound
+ * @return A new Sound object
+ */
+ public static function getAudioBuffer (id:String, useCache:Bool = true):AudioBuffer {
+
+ #if (tools && !display)
+
+ if (useCache && cache.enabled && cache.audio.exists (id)) {
+
+ var audio = cache.audio.get (id);
+
+ if (isValidAudio (audio)) {
+
+ return audio;
+
+ }
+
+ }
+
+ var libraryName = id.substring (0, id.indexOf (":"));
+ var symbolName = id.substr (id.indexOf (":") + 1);
+ var library = getLibrary (libraryName);
+
+ if (library != null) {
+
+ if (library.exists (symbolName, cast AssetType.SOUND)) {
+
+ if (library.isLocal (symbolName, cast AssetType.SOUND)) {
+
+ var audio = library.getAudioBuffer (symbolName);
+
+ if (useCache && cache.enabled) {
+
+ cache.audio.set (id, audio);
+
+ }
+
+ return audio;
+
+ } else {
+
+ Log.info ("Audio asset \"" + id + "\" exists, but only asynchronously");
+
+ }
+
+ } else {
+
+ Log.info ("There is no audio asset with an ID of \"" + id + "\"");
+
+ }
+
+ } else {
+
+ Log.info ("There is no asset library named \"" + libraryName + "\"");
+
+ }
+
+ #end
+
+ return null;
+
+ }
+
+
+ /**
+ * Gets an instance of an embedded binary asset
+ * @usage var bytes = Assets.getBytes("file.zip");
+ * @param id The ID or asset path for the file
+ * @return A new Bytes object
+ */
+ public static function getBytes (id:String):Bytes {
+
+ #if (tools && !display)
+
+ var libraryName = id.substring (0, id.indexOf(":"));
+ var symbolName = id.substr (id.indexOf(":") + 1);
+ var library = getLibrary (libraryName);
+
+ if (library != null) {
+
+ if (library.exists (symbolName, cast AssetType.BINARY)) {
+
+ if (library.isLocal (symbolName, cast AssetType.BINARY)) {
+
+ return library.getBytes (symbolName);
+
+ } else {
+
+ Log.info ("String or Bytes asset \"" + id + "\" exists, but only asynchronously");
+
+ }
+
+ } else {
+
+ Log.info ("There is no String or Bytes asset with an ID of \"" + id + "\"");
+
+ }
+
+ } else {
+
+ Log.info ("There is no asset library named \"" + libraryName + "\"");
+
+ }
+
+ #end
+
+ return null;
+
+ }
+
+
+ /**
+ * Gets an instance of an embedded font
+ * @usage var fontName = Assets.getFont("font.ttf").fontName;
+ * @param id The ID or asset path for the font
+ * @return A new Font object
+ */
+ public static function getFont (id:String, useCache:Bool = true):Font {
+
+ #if (tools && !display)
+
+ if (useCache && cache.enabled && cache.font.exists (id)) {
+
+ return cache.font.get (id);
+
+ }
+
+ var libraryName = id.substring (0, id.indexOf (":"));
+ var symbolName = id.substr (id.indexOf (":") + 1);
+ var library = getLibrary (libraryName);
+
+ if (library != null) {
+
+ if (library.exists (symbolName, cast AssetType.FONT)) {
+
+ if (library.isLocal (symbolName, cast AssetType.FONT)) {
+
+ var font = library.getFont (symbolName);
+
+ if (useCache && cache.enabled) {
+
+ cache.font.set (id, font);
+
+ }
+
+ return font;
+
+ } else {
+
+ Log.info ("Font asset \"" + id + "\" exists, but only asynchronously");
+
+ }
+
+ } else {
+
+ Log.info ("There is no Font asset with an ID of \"" + id + "\"");
+
+ }
+
+ } else {
+
+ Log.info ("There is no asset library named \"" + libraryName + "\"");
+
+ }
+
+ #end
+
+ return null;
+
+ }
+
+
+ /**
+ * Gets an instance of an embedded bitmap
+ * @usage var bitmap = new Bitmap(Assets.getBitmapData("image.jpg"));
+ * @param id The ID or asset path for the bitmap
+ * @param useCache (Optional) Whether to use BitmapData from the cache(Default: true)
+ * @return A new BitmapData object
+ */
+ public static function getImage (id:String, useCache:Bool = true):Image {
+
+ #if (tools && !display)
+
+ if (useCache && cache.enabled && cache.image.exists (id)) {
+
+ var image = cache.image.get (id);
+
+ if (isValidImage (image)) {
+
+ return image;
+
+ }
+
+ }
+
+ var libraryName = id.substring (0, id.indexOf (":"));
+ var symbolName = id.substr (id.indexOf (":") + 1);
+ var library = getLibrary (libraryName);
+
+ if (library != null) {
+
+ if (library.exists (symbolName, cast AssetType.IMAGE)) {
+
+ if (library.isLocal (symbolName, cast AssetType.IMAGE)) {
+
+ var image = library.getImage (symbolName);
+
+ if (useCache && cache.enabled) {
+
+ cache.image.set (id, image);
+
+ }
+
+ return image;
+
+ } else {
+
+ Log.info ("Image asset \"" + id + "\" exists, but only asynchronously");
+
+ }
+
+ } else {
+
+ Log.info ("There is no Image asset with an ID of \"" + id + "\"");
+
+ }
+
+ } else {
+
+ Log.info ("There is no asset library named \"" + libraryName + "\"");
+
+ }
+
+ #end
+
+ return null;
+
+ }
+
+
+ public static function getLibrary (name:String):AssetLibrary {
+
+ if (name == null || name == "") {
+
+ name = "default";
+
+ }
+
+ return libraries.get (name);
+
+ }
+
+
+ /**
+ * Gets the file path (if available) for an asset
+ * @usage var path = Assets.getPath("image.jpg");
+ * @param id The ID or asset path for the asset
+ * @return The path to the asset (or null)
+ */
+ public static function getPath (id:String):String {
+
+ #if (tools && !display)
+
+ var libraryName = id.substring (0, id.indexOf (":"));
+ var symbolName = id.substr (id.indexOf (":") + 1);
+ var library = getLibrary (libraryName);
+
+ if (library != null) {
+
+ if (library.exists (symbolName, null)) {
+
+ return library.getPath (symbolName);
+
+ } else {
+
+ Log.info ("There is no asset with an ID of \"" + id + "\"");
+
+ }
+
+ } else {
+
+ Log.info ("There is no asset library named \"" + libraryName + "\"");
+
+ }
+
+ #end
+
+ return null;
+
+ }
+
+
+ /**
+ * Gets an instance of an embedded text asset
+ * @usage var text = Assets.getText("text.txt");
+ * @param id The ID or asset path for the file
+ * @return A new String object
+ */
+ public static function getText (id:String):String {
+
+ #if (tools && !display)
+
+ var libraryName = id.substring (0, id.indexOf(":"));
+ var symbolName = id.substr (id.indexOf(":") + 1);
+ var library = getLibrary (libraryName);
+
+ if (library != null) {
+
+ if (library.exists (symbolName, cast AssetType.TEXT)) {
+
+ if (library.isLocal (symbolName, cast AssetType.TEXT)) {
+
+ return library.getText (symbolName);
+
+ } else {
+
+ Log.info ("String asset \"" + id + "\" exists, but only asynchronously");
+
+ }
+
+ } else {
+
+ Log.info ("There is no String asset with an ID of \"" + id + "\"");
+
+ }
+
+ } else {
+
+ Log.info ("There is no asset library named \"" + libraryName + "\"");
+
+ }
+
+ #end
+
+ return null;
+
+ }
+
+
+ public static function isLocal (id:String, type:AssetType = null, useCache:Bool = true):Bool {
+
+ #if (tools && !display)
+
+ if (useCache && cache.enabled) {
+
+ if (type == AssetType.IMAGE || type == null) {
+
+ if (cache.image.exists (id)) return true;
+
+ }
+
+ if (type == AssetType.FONT || type == null) {
+
+ if (cache.font.exists (id)) return true;
+
+ }
+
+ if (type == AssetType.SOUND || type == AssetType.MUSIC || type == null) {
+
+ if (cache.audio.exists (id)) return true;
+
+ }
+
+ }
+
+ var libraryName = id.substring (0, id.indexOf (":"));
+ var symbolName = id.substr (id.indexOf (":") + 1);
+ var library = getLibrary (libraryName);
+
+ if (library != null) {
+
+ return library.isLocal (symbolName, cast type);
+
+ }
+
+ #end
+
+ return false;
+
+ }
+
+
+ private static function isValidAudio (buffer:AudioBuffer):Bool {
+
+ #if (tools && !display)
+
+ return (buffer != null);
+ //return (sound.__handle != null && sound.__handle != 0);
+
+ #else
+
+ return true;
+
+ #end
+
+ }
+
+
+ private static function isValidImage (buffer:Image):Bool {
+
+ #if (tools && !display)
+ #if lime_cffi
+
+ return (buffer != null);
+ //return (bitmapData.__handle != null);
+
+ #elseif flash
+
+ /*try {
+
+ image.bytes.width;
+
+ } catch (e:Dynamic) {
+
+ return false;
+
+ }*/
+ return (buffer != null);
+
+ #end
+ #end
+
+ return true;
+
+ }
+
+
+ public static function list (type:AssetType = null):Array {
+
+ var items = [];
+
+ for (library in libraries) {
+
+ var libraryItems = library.list (cast type);
+
+ if (libraryItems != null) {
+
+ items = items.concat (libraryItems);
+
+ }
+
+ }
+
+ return items;
+
+ }
+
+
+ public static function loadAudioBuffer (id:String, useCache:Bool = true):Future {
+
+ var promise = new Promise ();
+
+ #if (tools && !display)
+
+ if (useCache && cache.enabled && cache.audio.exists (id)) {
+
+ var audio = cache.audio.get (id);
+
+ if (isValidAudio (audio)) {
+
+ promise.complete (audio);
+ return promise.future;
+
+ }
+
+ }
+
+ var libraryName = id.substring (0, id.indexOf (":"));
+ var symbolName = id.substr (id.indexOf (":") + 1);
+ var library = getLibrary (libraryName);
+
+ if (library != null) {
+
+ if (library.exists (symbolName, cast AssetType.SOUND)) {
+
+ var future = library.loadAudioBuffer (symbolName);
+
+ if (useCache && cache.enabled) {
+
+ future.onComplete (function (audio) cache.audio.set (id, audio));
+
+ }
+
+ promise.completeWith (future);
+
+ } else {
+
+ promise.error ("[Assets] There is no audio asset with an ID of \"" + id + "\"");
+
+ }
+
+ } else {
+
+ promise.error ("[Assets] There is no asset library named \"" + libraryName + "\"");
+
+ }
+
+ #end
+
+ return promise.future;
+
+ }
+
+
+ public static function loadBytes (id:String):Future {
+
+ var promise = new Promise ();
+
+ #if (tools && !display)
+
+ var libraryName = id.substring (0, id.indexOf (":"));
+ var symbolName = id.substr (id.indexOf (":") + 1);
+ var library = getLibrary (libraryName);
+
+ if (library != null) {
+
+ if (library.exists (symbolName, cast AssetType.BINARY)) {
+
+ promise.completeWith (library.loadBytes (symbolName));
+
+ } else {
+
+ promise.error ("[Assets] There is no String or Bytes asset with an ID of \"" + id + "\"");
+
+ }
+
+ } else {
+
+ promise.error ("[Assets] There is no asset library named \"" + libraryName + "\"");
+
+ }
+
+ #end
+
+ return promise.future;
+
+ }
+
+
+ public static function loadFont (id:String):Future {
+
+ var promise = new Promise ();
+
+ #if (tools && !display)
+
+ var libraryName = id.substring (0, id.indexOf (":"));
+ var symbolName = id.substr (id.indexOf (":") + 1);
+ var library = getLibrary (libraryName);
+
+ if (library != null) {
+
+ if (library.exists (symbolName, cast AssetType.FONT)) {
+
+ promise.completeWith (library.loadFont (symbolName));
+
+ } else {
+
+ promise.error ("[Assets] There is no Font asset with an ID of \"" + id + "\"");
+
+ }
+
+ } else {
+
+ promise.error ("[Assets] There is no asset library named \"" + libraryName + "\"");
+
+ }
+
+ #end
+
+ return promise.future;
+
+ }
+
+
+ public static function loadImage (id:String, useCache:Bool = true):Future {
+
+ var promise = new Promise ();
+
+ #if (tools && !display)
+
+ if (useCache && cache.enabled && cache.image.exists (id)) {
+
+ var image = cache.image.get (id);
+
+ if (isValidImage (image)) {
+
+ promise.complete (image);
+ return promise.future;
+
+ }
+
+ }
+
+ var libraryName = id.substring (0, id.indexOf (":"));
+ var symbolName = id.substr (id.indexOf (":") + 1);
+ var library = getLibrary (libraryName);
+
+ if (library != null) {
+
+ if (library.exists (symbolName, cast AssetType.IMAGE)) {
+
+ var future = library.loadImage (symbolName);
+
+ if (useCache && cache.enabled) {
+
+ future.onComplete (function (image) cache.image.set (id, image));
+
+ }
+
+ promise.completeWith (future);
+
+ } else {
+
+ promise.error ("[Assets] There is no Image asset with an ID of \"" + id + "\"");
+
+ }
+
+ } else {
+
+ promise.error ("[Assets] There is no asset library named \"" + libraryName + "\"");
+
+ }
+
+ #end
+
+ return promise.future;
+
+ }
+
+
+ public static function loadLibrary (name:String):Future {
+
+ var promise = new Promise ();
+
+ #if (tools && !display)
+
+ var data = getText ("libraries/" + name + ".json");
+
+ if (data != null && data != "") {
+
+ var info = Json.parse (data);
+ var library = Type.createInstance (Type.resolveClass (info.type), info.args);
+ libraries.set (name, library);
+ library.onChange.add (onChange.dispatch);
+ promise.completeWith (library.load ());
+
+ } else {
+
+ promise.error ("[Assets] There is no asset library named \"" + name + "\"");
+
+ }
+
+ #end
+
+ return promise.future;
+
+ }
+
+
+ public static function loadText (id:String):Future {
+
+ var promise = new Promise ();
+
+ #if (tools && !display)
+
+ var libraryName = id.substring (0, id.indexOf (":"));
+ var symbolName = id.substr (id.indexOf (":") + 1);
+ var library = getLibrary (libraryName);
+
+ if (library != null) {
+
+ if (library.exists (symbolName, cast AssetType.TEXT)) {
+
+ promise.completeWith (library.loadText (symbolName));
+
+ } else {
+
+ promise.error ("[Assets] There is no String asset with an ID of \"" + id + "\"");
+
+ }
+
+ } else {
+
+ promise.error ("[Assets] There is no asset library named \"" + libraryName + "\"");
+
+ }
+
+ #end
+
+ return promise.future;
+
+ }
+
+
+ public static function registerLibrary (name:String, library:AssetLibrary):Void {
+
+ if (libraries.exists (name)) {
+
+ if (libraries.get (name) == library) {
+
+ return;
+
+ } else {
+
+ unloadLibrary (name);
+
+ }
+
+ }
+
+ if (library != null) {
+
+ library.onChange.add (library_onChange);
+
+ }
+
+ libraries.set (name, library);
+
+ }
+
+
+ public static function unloadLibrary (name:String):Void {
+
+ #if (tools && !display)
+
+ var library = libraries.get (name);
+
+ if (library != null) {
+
+ cache.clear (name + ":");
+ library.onChange.remove (library_onChange);
+ library.unload ();
+
+ }
+
+ libraries.remove (name);
+
+ #end
+
+ }
+
+
+
+
+ // Event Handlers
+
+
+
+
+ private static function library_onChange ():Void {
+
+ cache.clear ();
+ onChange.dispatch ();
+
+ }
+
+}
\ No newline at end of file
diff --git a/lime/utils/Bytes.hx b/lime/utils/Bytes.hx
index 7c87c4aeb..a1cd5ac1d 100644
--- a/lime/utils/Bytes.hx
+++ b/lime/utils/Bytes.hx
@@ -3,23 +3,25 @@ package lime.utils;
import haxe.io.Bytes in HaxeBytes;
import haxe.io.BytesData;
+import lime.app.Future;
#if !macro
@:build(lime.system.CFFI.build())
#end
-@:autoBuild(lime.Assets.embedBytes())
+@:access(haxe.io.Bytes)
+@:forward()
-class Bytes extends HaxeBytes {
+abstract Bytes(HaxeBytes) from HaxeBytes to HaxeBytes {
public function new (length:Int, bytesData:BytesData) {
#if js
- super (bytesData);
+ this = new HaxeBytes (bytesData);
#else
- super (length, bytesData);
+ this = new HaxeBytes (length, bytesData);
#end
}
@@ -47,6 +49,31 @@ class Bytes extends HaxeBytes {
}
+ public static function fromFile (path:String):Bytes {
+
+ #if (!html5 && !macro)
+ var data:Dynamic = lime_bytes_read_file (path);
+ if (data != null) return new Bytes (data.length, data.b);
+ #end
+ return null;
+
+ }
+
+
+ public static function loadFromBytes (bytes:haxe.io.Bytes):Future {
+
+ return Future.withValue (fromBytes (bytes));
+
+ }
+
+
+ public static function loadFromFile (path:String):Future {
+
+ return new Future (function () return fromFile (path), true);
+
+ }
+
+
public static function ofData (b:BytesData):Bytes {
var bytes = HaxeBytes.ofData (b);
@@ -63,17 +90,6 @@ class Bytes extends HaxeBytes {
}
- public static function readFile (path:String):Bytes {
-
- #if (!html5 && !macro)
- var data:Dynamic = lime_bytes_read_file (path);
- if (data != null) return new Bytes (data.length, data.b);
- #end
- return null;
-
- }
-
-
#if (lime_cffi && !macro)
public static function __fromNativePointer (data:Dynamic, length:Int):Bytes {
diff --git a/templates/haxe/ApplicationMain.hx b/templates/haxe/ApplicationMain.hx
index bdbca0e50..0f8010bd6 100644
--- a/templates/haxe/ApplicationMain.hx
+++ b/templates/haxe/ApplicationMain.hx
@@ -62,8 +62,10 @@ class ApplicationMain {
public static function create ():Void {
+ var library = new DefaultAssetLibrary ();
+ lime.utils.Assets.registerLibrary ("default", library);
+
preloader = new ::if (PRELOADER_NAME != "")::::PRELOADER_NAME::::else::lime.app.Preloader::end:: ();
- preloader.onComplete.add (registerLibrary);
#if !munit
app = new ::APP_MAIN:: ();
@@ -71,21 +73,24 @@ class ApplicationMain {
app.create (config);
#end
- preloader.onComplete.add (start);
preloader.create (config);
+ preloader.addLibrary (library);
+ preloader.load ();
- #if (js && html5)
+ start ();
+
+ /*#if (js && html5)
var urls = [];
var types = [];
::foreach assets::::if (embed)::
urls.push ("::resourceName::");
- ::if (type == "image")::types.push (lime.Assets.AssetType.IMAGE);
- ::elseif (type == "binary")::types.push (lime.Assets.AssetType.BINARY);
- ::elseif (type == "text")::types.push (lime.Assets.AssetType.TEXT);
- ::elseif (type == "font")::types.push (lime.Assets.AssetType.FONT);
- ::elseif (type == "sound")::types.push (lime.Assets.AssetType.SOUND);
- ::elseif (type == "music")::types.push (lime.Assets.AssetType.MUSIC);
+ ::if (type == "image")::types.push (lime.utils.AssetType.IMAGE);
+ ::elseif (type == "binary")::types.push (lime.utils.AssetType.BINARY);
+ ::elseif (type == "text")::types.push (lime.utils.AssetType.TEXT);
+ ::elseif (type == "font")::types.push (lime.utils.AssetType.FONT);
+ ::elseif (type == "sound")::types.push (lime.utils.AssetType.SOUND);
+ ::elseif (type == "music")::types.push (lime.utils.AssetType.MUSIC);
::else::types.push (null);::end::
::end::::end::
@@ -93,7 +98,7 @@ class ApplicationMain {
for (i in 0...urls.length) {
- if (types[i] != lime.Assets.AssetType.FONT) {
+ if (types[i] != lime.utils.AssetType.FONT) {
urls[i] = config.assetsPrefix + urls[i];
@@ -104,7 +109,7 @@ class ApplicationMain {
}
preloader.load (urls, types);
- #end
+ #end*/
}
@@ -171,13 +176,6 @@ class ApplicationMain {
#end
- private static function registerLibrary ():Void {
-
- lime.Assets.registerLibrary ("default", new DefaultAssetLibrary ());
-
- }
-
-
public static function start ():Void {
#if !munit
diff --git a/templates/haxe/DefaultAssetLibrary.hx b/templates/haxe/DefaultAssetLibrary.hx
index 741da18f4..def996302 100644
--- a/templates/haxe/DefaultAssetLibrary.hx
+++ b/templates/haxe/DefaultAssetLibrary.hx
@@ -3,46 +3,20 @@ package;
import haxe.Timer;
import haxe.Unserializer;
-import lime.app.Future;
-import lime.app.Preloader;
-import lime.app.Promise;
-import lime.audio.AudioSource;
-import lime.audio.openal.AL;
-import lime.audio.AudioBuffer;
-import lime.graphics.Image;
-import lime.net.HTTPRequest;
-import lime.system.CFFI;
-import lime.text.Font;
+import lime.utils.AssetLibrary;
+import lime.utils.AssetType;
import lime.utils.Bytes;
import lime.utils.Log;
-import lime.utils.UInt8Array;
-import lime.Assets;
#if sys
import haxe.io.Path;
import sys.FileSystem;
#end
-#if flash
-import flash.display.Bitmap;
-import flash.display.BitmapData;
-import flash.display.Loader;
-import flash.events.Event;
-import flash.events.IOErrorEvent;
-import flash.events.ProgressEvent;
-import flash.media.Sound;
-import flash.net.URLLoader;
-import flash.net.URLRequest;
-#end
-
@:keep class DefaultAssetLibrary extends AssetLibrary {
- public var className (default, null) = new Map ();
- public var path (default, null) = new Map ();
- public var type (default, null) = new Map ();
-
private var lastModified:Float;
private var timer:Timer;
@@ -65,17 +39,17 @@ import flash.net.URLRequest;
#if flash
- ::if (assets != null)::::foreach assets::::if (embed)::className.set ("::id::", __ASSET__::flatName::);::else::path.set ("::id::", "::resourceName::");::end::
- type.set ("::id::", AssetType.$$upper(::type::));
+ ::if (assets != null)::::foreach assets::::if (embed)::classTypes.set ("::id::", __ASSET__::flatName::);::else::paths.set ("::id::", "::resourceName::");::end::
+ types.set ("::id::", AssetType.$$upper(::type::));
::end::::end::
#elseif html5
::if (assets != null)::var id;
::foreach assets::id = "::id::";
- ::if (embed)::::if (type == "font")::className.set (id, __ASSET__::flatName::);::else::path.set (id, ::if (resourceName == id)::id::else::"::resourceName::"::end::);::end::
- ::else::path.set (id, ::if (resourceName == id)::id::else::"::resourceName::"::end::);::end::
- type.set (id, AssetType.$$upper(::type::));
+ ::if (embed)::::if (type == "font")::classTypes.set (id, __ASSET__::flatName::);::else::paths.set (id, ::if (resourceName == id)::id::else::"::resourceName::"::end::);::end::
+ ::else::paths.set (id, ::if (resourceName == id)::id::else::"::resourceName::"::end::);::end::
+ types.set (id, AssetType.$$upper(::type::));
::end::::end::
var assetsPrefix = null;
@@ -83,22 +57,27 @@ import flash.net.URLRequest;
assetsPrefix = ApplicationMain.config.assetsPrefix;
}
if (assetsPrefix != null) {
- for (k in path.keys()) {
- path.set(k, assetsPrefix + path[k]);
+ for (k in paths.keys()) {
+ paths.set(k, assetsPrefix + paths[k]);
}
}
+ for (id in paths.keys()) {
+ trace (id);
+ preload.set (id, true);
+ }
+
#else
#if (windows || mac || linux)
var useManifest = false;
::if (assets != null)::::foreach assets::::if (type == "font")::
- className.set ("::id::", __ASSET__::flatName::);
- type.set ("::id::", AssetType.$$upper(::type::));
+ classTypes.set ("::id::", __ASSET__::flatName::);
+ types.set ("::id::", AssetType.$$upper(::type::));
::else::::if (embed)::
- className.set ("::id::", __ASSET__::flatName::);
- type.set ("::id::", AssetType.$$upper(::type::));
+ classTypes.set ("::id::", __ASSET__::flatName::);
+ types.set ("::id::", AssetType.$$upper(::type::));
::else::useManifest = true;
::end::::end::::end::::end::
@@ -106,7 +85,7 @@ import flash.net.URLRequest;
loadManifest ();
- if (Sys.args ().indexOf ("-livereload") > -1) {
+ if (false && Sys.args ().indexOf ("-livereload") > -1) {
var path = FileSystem.fullPath ("manifest");
@@ -146,487 +125,23 @@ import flash.net.URLRequest;
}
- public override function exists (id:String, type:String):Bool {
-
- var requestedType = type != null ? cast (type, AssetType) : null;
- var assetType = this.type.get (id);
-
- if (assetType != null) {
-
- if (assetType == requestedType || ((requestedType == SOUND || requestedType == MUSIC) && (assetType == MUSIC || assetType == SOUND))) {
-
- return true;
-
- }
-
- #if flash
-
- if (requestedType == BINARY && (assetType == BINARY || assetType == TEXT || assetType == IMAGE)) {
-
- return true;
-
- } else if (requestedType == TEXT && assetType == BINARY) {
-
- return true;
-
- } else if (requestedType == null || path.exists (id)) {
-
- return true;
-
- }
-
- #else
-
- if (requestedType == BINARY || requestedType == null || (assetType == BINARY && requestedType == TEXT)) {
-
- return true;
-
- }
-
- #end
-
- }
-
- return false;
-
- }
-
-
- public override function getAudioBuffer (id:String):AudioBuffer {
-
- #if flash
-
- var buffer = new AudioBuffer ();
- buffer.src = cast (Type.createInstance (className.get (id), []), Sound);
- return buffer;
-
- #elseif html5
-
- return Preloader.audioBuffers.get (path.get (id));
-
- #else
-
- if (className.exists (id)) return AudioBuffer.fromBytes (cast (Type.createInstance (className.get (id), []), Bytes));
- else return AudioBuffer.fromFile (rootPath + path.get (id));
-
- #end
-
- }
-
-
- public override function getBytes (id:String):Bytes {
-
- #if flash
-
- switch (type.get (id)) {
-
- case TEXT, BINARY:
-
- return Bytes.ofData (cast (Type.createInstance (className.get (id), []), flash.utils.ByteArray));
-
- case IMAGE:
-
- var bitmapData = cast (Type.createInstance (className.get (id), []), BitmapData);
- return Bytes.ofData (bitmapData.getPixels (bitmapData.rect));
-
- default:
-
- return null;
-
- }
-
- return cast (Type.createInstance (className.get (id), []), Bytes);
-
- #elseif html5
-
- var loader = Preloader.loaders.get (path.get (id));
-
- if (loader == null) {
-
- return null;
-
- }
-
- var bytes:Bytes = cast loader.responseData;
-
- if (bytes != null) {
-
- return bytes;
-
- } else {
-
- return null;
- }
-
- #else
-
- if (className.exists(id)) return cast (Type.createInstance (className.get (id), []), Bytes);
- else return Bytes.readFile (rootPath + path.get (id));
-
- #end
-
- }
-
-
- public override function getFont (id:String):Font {
-
- #if flash
-
- var src = Type.createInstance (className.get (id), []);
-
- var font = new Font (src.fontName);
- font.src = src;
- return font;
-
- #elseif html5
-
- return cast (Type.createInstance (className.get (id), []), Font);
-
- #else
-
- if (className.exists (id)) {
-
- var fontClass = className.get (id);
- return cast (Type.createInstance (fontClass, []), Font);
-
- } else {
-
- return Font.fromFile (rootPath + path.get (id));
-
- }
-
- #end
-
- }
-
-
- public override function getImage (id:String):Image {
-
- #if flash
-
- return Image.fromBitmapData (cast (Type.createInstance (className.get (id), []), BitmapData));
-
- #elseif html5
-
- return Image.fromImageElement (Preloader.images.get (path.get (id)));
-
- #else
-
- if (className.exists (id)) {
-
- var fontClass = className.get (id);
- return cast (Type.createInstance (fontClass, []), Image);
-
- } else {
-
- return Image.fromFile (rootPath + path.get (id));
-
- }
-
- #end
-
- }
-
-
- /*public override function getMusic (id:String):Dynamic {
-
- #if flash
-
- return cast (Type.createInstance (className.get (id), []), Sound);
-
- #elseif openfl_html5
-
- //var sound = new Sound ();
- //sound.__buffer = true;
- //sound.load (new URLRequest (path.get (id)));
- //return sound;
- return null;
-
- #elseif html5
-
- return null;
- //return new Sound (new URLRequest (path.get (id)));
-
- #else
-
- return null;
- //if (className.exists(id)) return cast (Type.createInstance (className.get (id), []), Sound);
- //else return new Sound (new URLRequest (path.get (id)), null, true);
-
- #end
-
- }*/
-
-
- public override function getPath (id:String):String {
-
- //#if ios
-
- //return SystemPath.applicationDirectory + "/assets/" + path.get (id);
-
- //#else
-
- return path.get (id);
-
- //#end
-
- }
-
-
- public override function getText (id:String):String {
-
- #if html5
-
- var loader = Preloader.loaders.get (path.get (id));
-
- if (loader == null) {
-
- return null;
-
- }
-
- var bytes:Bytes = cast loader.responseData;
-
- if (bytes != null) {
-
- return bytes.getString (0, bytes.length);
-
- } else {
-
- return null;
- }
-
- #else
-
- var bytes = getBytes (id);
-
- if (bytes == null) {
-
- return null;
-
- } else {
-
- return bytes.getString (0, bytes.length);
-
- }
-
- #end
-
- }
-
-
- public override function isLocal (id:String, type:String):Bool {
-
- #if flash
-
- return className.exists (id);
-
- #elseif html5
-
- var requestedType = type != null ? cast (type, AssetType) : null;
-
- return switch (requestedType) {
-
- case FONT:
-
- className.exists (id);
-
- case IMAGE:
-
- Preloader.images.exists (path.get (id));
-
- case MUSIC, SOUND:
-
- Preloader.audioBuffers.exists (path.get (id));
-
- default:
-
- Preloader.loaders.exists (path.get (id));
-
- }
-
- #else
-
- return true;
-
- #end
-
- }
-
-
- public override function list (type:String):Array {
-
- var requestedType = type != null ? cast (type, AssetType) : null;
- var items = [];
-
- for (id in this.type.keys ()) {
-
- if (requestedType == null || exists (id, type)) {
-
- items.push (id);
-
- }
-
- }
-
- return items;
-
- }
-
-
- public override function loadAudioBuffer (id:String):Future {
-
- var promise = new Promise ();
-
- if (Assets.isLocal (id)) {
-
- promise.completeWith (new Future (function () return getAudioBuffer (id), true));
-
- } else if (path.exists (id)) {
-
- promise.completeWith (AudioBuffer.loadFromFile (path.get (id)));
-
- } else {
-
- promise.error (null);
-
- }
-
- return promise.future;
-
- }
-
-
- public override function loadBytes (id:String):Future {
-
- var promise = new Promise ();
-
- #if flash
-
- if (path.exists (id)) {
-
- var loader = new URLLoader ();
- loader.dataFormat = flash.net.URLLoaderDataFormat.BINARY;
- loader.addEventListener (Event.COMPLETE, function (event:Event) {
-
- var bytes = Bytes.ofData (loader.data);
- promise.complete (bytes);
-
- });
- loader.addEventListener (ProgressEvent.PROGRESS, function (event) {
-
- promise.progress (event.bytesLoaded, event.bytesTotal);
-
- });
- loader.addEventListener (IOErrorEvent.IO_ERROR, promise.error);
- loader.load (new URLRequest (path.get (id)));
-
- } else {
-
- promise.complete (getBytes (id));
-
- }
-
- #elseif html5
-
- if (path.exists (id)) {
-
- var request = new HTTPRequest ();
- promise.completeWith (request.load (path.get (id) + "?" + Assets.cache.version));
-
- } else {
-
- promise.complete (getBytes (id));
-
- }
-
- #else
-
- promise.completeWith (new Future (function () return getBytes (id), true));
-
- #end
-
- return promise.future;
-
- }
-
-
- public override function loadImage (id:String):Future {
-
- var promise = new Promise ();
-
- #if flash
-
- if (path.exists (id)) {
-
- var loader = new Loader ();
- loader.contentLoaderInfo.addEventListener (Event.COMPLETE, function (event:Event) {
-
- var bitmapData = cast (loader.content, Bitmap).bitmapData;
- promise.complete (Image.fromBitmapData (bitmapData));
-
- });
- loader.contentLoaderInfo.addEventListener (ProgressEvent.PROGRESS, function (event) {
-
- promise.progress (event.bytesLoaded, event.bytesTotal);
-
- });
- loader.contentLoaderInfo.addEventListener (IOErrorEvent.IO_ERROR, promise.error);
- loader.load (new URLRequest (path.get (id)));
-
- } else {
-
- promise.complete (getImage (id));
-
- }
-
- #elseif html5
-
- if (path.exists (id)) {
-
- var image = new js.html.Image ();
- image.onload = function (_):Void {
-
- promise.complete (Image.fromImageElement (image));
-
- }
- image.onerror = promise.error;
- image.src = path.get (id) + "?" + Assets.cache.version;
-
- } else {
-
- promise.complete (getImage (id));
-
- }
-
- #else
-
- promise.completeWith (new Future (function () return getImage (id), true));
-
- #end
-
- return promise.future;
-
- }
-
-
#if (!flash && !html5)
private function loadManifest ():Void {
try {
#if blackberry
- var bytes = Bytes.readFile ("app/native/manifest");
+ var bytes = Bytes.fromFile ("app/native/manifest");
#elseif tizen
- var bytes = Bytes.readFile ("../res/manifest");
+ var bytes = Bytes.fromFile ("../res/manifest");
#elseif emscripten
- var bytes = Bytes.readFile ("assets/manifest");
+ var bytes = Bytes.fromFile ("assets/manifest");
#elseif (mac && java)
- var bytes = Bytes.readFile ("../Resources/manifest");
+ var bytes = Bytes.fromFile ("../Resources/manifest");
#elseif (ios || tvos)
- var bytes = Bytes.readFile ("assets/manifest");
+ var bytes = Bytes.fromFile ("assets/manifest");
#else
- var bytes = Bytes.readFile ("manifest");
+ var bytes = Bytes.fromFile ("manifest");
#end
if (bytes != null) {
@@ -641,14 +156,14 @@ import flash.net.URLRequest;
for (asset in manifest) {
- if (!className.exists (asset.id)) {
+ if (!classTypes.exists (asset.id)) {
#if (ios || tvos)
- path.set (asset.id, "assets/" + asset.path);
+ paths.set (asset.id, rootPath + "assets/" + asset.path);
#else
- path.set (asset.id, asset.path);
+ paths.set (asset.id, rootPath + asset.path);
#end
- type.set (asset.id, cast (asset.type, AssetType));
+ types.set (asset.id, cast (asset.type, AssetType));
}
@@ -674,50 +189,6 @@ import flash.net.URLRequest;
#end
- public override function loadText (id:String):Future {
-
- var promise = new Promise ();
-
- #if html5
-
- if (path.exists (id)) {
-
- var request = new HTTPRequest ();
- promise.completeWith (request.load (path.get (id) + "?" + Assets.cache.version));
-
- } else {
-
- promise.complete (getText (id));
-
- }
-
- #else
-
- promise.completeWith (loadBytes (id).then (function (bytes) {
-
- return new Future (function () {
-
- if (bytes == null) {
-
- return null;
-
- } else {
-
- return bytes.getString (0, bytes.length);
-
- }
-
- }, true);
-
- }));
-
- #end
-
- return promise.future;
-
- }
-
-
}