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; - - } - - }