Forget to add SetGraphics stub for cpp targets...

#if HOST="macos" And TARGET="glfw"
	Import "native/diddy.${TARGET}.mac.${LANG}"
#else
	Import "native/diddy.${TARGET}.${LANG}"
#end
Import mojo
Import assert

Extern

	#If LANG="cpp" Then
		Function RealMillisecs:Int() = "diddy::systemMillisecs"
		Function FlushKeys:Void() = "diddy::flushKeys"
		Function HideMouse:Void() = "diddy::hideMouse"
		Function ShowMouse:Void() = "diddy::showMouse"
		Function GetUpdateRate:Int() = "diddy::getUpdateRate"
		Function SetMouse:Void(x:Int, y:Int) = "diddy::setMouse"
		Function ShowKeyboard:Void() = "diddy::showKeyboard"
		Function LaunchBrowser:Void(address:String) = "diddy::launchBrowser"
		Function LaunchEmail:Void(email:String, subject:String, text:String) = "diddy::launchEmail"
		Function SetGraphics:Void(w:Int, h:Int) = "diddy::setGraphics"
	#Else
		Function RealMillisecs:Int() = "diddy.systemMillisecs"
		Function FlushKeys:Void() = "diddy.flushKeys"
		Function HideMouse:Void() = "diddy.hideMouse"
		Function ShowMouse:Void() = "diddy.showMouse"
		Function GetUpdateRate:Int() = "diddy.getUpdateRate"
		Function SetMouse:Void(x:Int, y:Int) = "diddy.setMouse"
		Function ShowKeyboard:Void() = "diddy.showKeyboard"
		Function LaunchBrowser:Void(address:String) = "diddy.launchBrowser"
		Function LaunchEmail:Void(email:String, subject:String, text:String) = "diddy.launchEmail"
		Function SetGraphics:Void(w:Int, h:Int) = "diddy.setGraphics"
	#End
Public

Function ExitApp:Void()
	Error ""
End

Function RectsOverlap:Int(x0:Float, y0:Float, w0:Float, h0:Float, x2:Float, y2:Float, w2:Float, h2:Float)
	If x0 > (x2 + w2) Or (x0 + w0) < x2 Then Return False
	If y0 > (y2 + h2) Or (y0 + h0) < y2 Then Return False
	Return True
End

Function DrawRectOutline:Void(x:Int, y:Int, w:Int, h:Int)
	w -= 1
	h -= 1
	DrawLine(x,y,x+w,y)
	DrawLine(x+w,y,x+w,y+h)
	DrawLine(x+w,y+h,x,y+h)
	DrawLine(x,y+h,x,y)
End

Function LoadBitmap:Image(path$, flags%=0)
	Local pointer:Image = LoadImage(path, 1, flags)

	AssertNotNull(pointer, "Error loading bitmap "+path)

   	Return pointer
End

Function LoadAnimBitmap:Image(path$, w%, h%, count%, tmpImage:Image)
	'tmpImage = loadBitmap(path) <-- This creates another image, decided to just copy the code here
	tmpImage = LoadImage(path)

	AssertNotNull(tmpImage, "Error loading bitmap "+path)

	Local pointer:Image = tmpImage.GrabImage( 0, 0, w, h, count, Image.MidHandle)

   	Return pointer
End

Function LoadSoundSample:Sound(path$)
	Local pointer:Sound = LoadSound(path)
	AssertNotNull(pointer, "Error loading sound "+path)
	Return pointer
End

Function FormatNumeric:String(value:Float)
	Local i:Int,s:String,ns:String,k:Int
	Local os:String
	s=String(value)
	os=s
	Local pos:Int=s.Length()
	If s.Find(".")>0 pos=s.Find(".") Else os=""
	For i=pos To 1 Step -1
		If k>2 ns+="." k=0
		k+=1
		ns=ns+Mid(s,i,1)
	Next
	s=""
	For i= ns.Length() To 1 Step -1
		s+=Mid(ns,i,1)
	Next
	If os<>"" s=s+","+os[pos+1..]
	Return s
End

Function Left$( str$,n:Int )
	If n>str.Length() n=str.Length()
	Return str[..n]
End

Function Right$( str$,n:Int )
	If n>str.Length() n=str.Length()
	Return str[str.Length()-n..]
End

Function LSet$( str$,n:Int,char:String=" " )
	Local rep:String
	For Local i:Int=1 To n
		rep=rep+char
	Next
	str=str+rep
	Return str[..n]
End

Function RSet$( str$,n:Int,char:String=" " )
	Local rep:String
	For Local i:Int=1 To n
		rep=rep+char
	Next
	str=rep+str
	Return str[str.Length()-n..]
End

Function Mid$( str$,pos:Int,size:Int=-1 )
	If pos>str.Length() Return ""
	pos-=1
	If( size<0 ) Return str[pos..]
	If pos<0 size=size+pos pos=0
	If pos+size>str.Length() size=str.Length()-pos
	Return str[pos..pos+size]
End

Function StripDir$( path$ )
	Local i:=path.FindLast( "/" )
	If i<>-1 Return path[i+1..]
	Return path
End

Function StripExt$( path$ )
	Local i:=path.FindLast( "." )
	If i<>-1 And path.Find( "/",i+1 )=-1 Return path[..i]
	Return path
End

Function StripAll$( path$ )
	Return StripDir( StripExt( path ) )
End

Function Round%(flot#)
	Return Floor(flot+0.5)
End

Function PointInSpot:Int(x1:Float, y1:Float, x2:Float, y2:Float, radius:Float)
	Local dx:Float = x2 - x1
	Local dy:Float = y2 - y1
	Return dx * dx + dy * dy <= radius * radius
End

Function AnyInputPressed:Bool()
	For Local i:Int = 0 To 511
		If KeyHit(i) Then Return True
	Next
	Return False
End

Function FormatNumber:String(number:Float, decimal:Int=4, comma:Int=0, padleft:Int=0 )
	Assert(decimal > -1 And comma > -1 And padleft > -1, "Negative numbers not allowed in FormatNumber()")

	Local str:String = number
	Local dl:Int = str.Find(".")
	If decimal = 0 Then decimal = -1
	str = str[..dl+decimal+1]

	If comma
		While dl>comma
			str = str[..dl-comma] + "," + str[dl-comma..]
			dl -= comma
		Wend
	End

	If padleft
		Local paddedLength:Int = padleft+decimal+1
		If paddedLength < str.Length Then str = "Error"
		str = RSet(str,paddedLength)
	End
	Return str
End

Function IsWhitespace:Bool(str:String)
	Return str = "~t" Or str = "~n" Or str = "~r" Or str = " "
End

Function IsWhitespace:Bool(val:Int)
	Return val = ASC_TAB Or val = ASC_LF Or val = ASC_CR Or val = ASC_SPACE
End

' TODO: allow encoding of Int[]
' TODO: enabling lineWrap will wrap the string every lineWrapWidth characters (defaults to 80)
' enabling padOutput will pad the ending of the output with equals characters (=) to next 4-byte boundary
Function EncodeBase64:String(src:String, padOutput:Bool=False, lineWrap:Bool=False, lineWrapWidth:Int=80)
	Local rv:String = ""
	Local s1:Int, s2:Int, s3:Int, a:Int, b:Int, c:Int, d:Int, i:Int
	If src.Length = 0 Then Return ""
	Repeat
		s1 = src[i]
		If i+1 < src.Length Then s2 = src[i+1] Else s2 = 0
		If i+2 < src.Length Then s3 = src[i+2] Else s3 = 0
		a = s1 Shr 2
		b = ((s1 & 3) Shl 4) | (s2 Shr 4)
		c = ((s2 & 15) Shl 2) | (s3 Shr 6)
		d = s3 & 63
		If i+1 >= src.Length Then c = 64
		If i+2 >= src.Length Then d = 64
		rv += BASE64_CHARS[a..a+1] + BASE64_CHARS[b..b+1]
		If c < 64 Or padOutput Then rv += BASE64_CHARS[c..c+1]
		If d < 64 Or padOutput Then rv += BASE64_CHARS[d..d+1]
		i += 3
	Until i >= src.Length
	Return rv
End

' TODO: convert this to use BASE64_ARRAY
' I'll do this once Monkey has a decent string builder class.
Function DecodeBase64:String(src:String)
	Local rv:String = "", src2:String = ""
	Local s1:Int, s2:Int, s3:Int, a:Int, b:Int, c:Int, d:Int, i:Int
	' remove any non-base64 characters
	For i = 0 Until src.Length
		If BASE64_CHARS.Find(src[i..i+1]) >= 0 Then src2 += src[i..i+1]
	Next
	If src2.Length = 0 Then Return ""
	i = 0
	Repeat
		a = BASE64_CHARS.Find(src2[i..i+1])
		If i+1 > src2.Length Then Exit ' This shouldn't happen with base64, so something's wrong!
		b = BASE64_CHARS.Find(src2[i+1..i+2])
		If i+2 < src2.Length Then c = BASE64_CHARS.Find(src2[i+2..i+3]) Else c = 64
		If i+3 < src2.Length Then d = BASE64_CHARS.Find(src2[i+3..i+4]) Else d = 64
		s1 = (a Shl 2) | (b Shr 4)
		s2 = ((b & 15) Shl 4) | (c Shr 2)
		s3 = ((c & 3) Shl 6) | d
		rv += String.FromChar(s1)
		If c <> 64 Then rv += String.FromChar(s2)
		If d <> 64 Then rv += String.FromChar(s3)
		i += 4
	Until i >= src2.Length
	Return rv
End

Function DecodeBase64Bytes:Int[](src:String)
	InitBase64()
	Local a:Int, b:Int, c:Int, d:Int, i:Int, j:Int
	Local src2:Int[] = New Int[src.Length]

	' find out how many base64 characters
	Local srclen:Int = 0
	For i = 0 Until src.Length
		If BASE64_ARRAY[src[i]] >= 0 Then
			src2[srclen] = src[i]
			srclen += 1
		End
	Next

	' die if there are no base64 chars
	If srclen = 0 Then Return []

	' get the target length and create the array
	Local len:Int = 3*srclen/4
	If srclen Mod 4 = 1 Or srclen Mod 4 = 2 Then
		len += 1
	ElseIf srclen Mod 4 = 3 Then
		len += 2
	End
	Local rv:Int[] = New Int[len]

	i = 0
	j = 0
	Repeat
		a = BASE64_ARRAY[src2[i]]
		If i+1 > srclen Then Exit ' This shouldn't happen with base64, so something's wrong!
		b = BASE64_ARRAY[src2[i+1]]
		If i+2 < srclen Then c = BASE64_ARRAY[src2[i+2]] Else c = 64
		If i+3 < srclen Then d = BASE64_ARRAY[src2[i+3]] Else d = 64
		rv[j] = (a Shl 2) | (b Shr 4)
		If j+1 < len Then rv[j+1] = ((b & 15) Shl 4) | (c Shr 2)
		If j+2 < len Then rv[j+2] = ((c & 3) Shl 6) | d
		i += 4
		j += 3
	Until i >= srclen
	Return rv
End

Function InitBase64:Void()
	If BASE64_ARRAY.Length = 0 Then
		BASE64_ARRAY = New Int[256]
		Local i% = 0
		For i = 0 Until BASE64_ARRAY.Length
			BASE64_ARRAY[i] = -1
		Next
		For i = 0 Until BASE64_CHARS.Length
			BASE64_ARRAY[BASE64_CHARS[i]] = i
		Next
	End
End

' constants

Const BASE64_CHARS:String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
Global BASE64_ARRAY:Int[] = []

' control characters
Const ASC_NUL:Int = 0       ' Null character
Const ASC_SOH:Int = 1       ' Start of Heading
Const ASC_STX:Int = 2       ' Start of Text
Const ASC_ETX:Int = 3       ' End of Text
Const ASC_EOT:Int = 4       ' End of Transmission
Const ASC_ENQ:Int = 5       ' Enquiry
Const ASC_ACK:Int = 6       ' Acknowledgment
Const ASC_BEL:Int = 7       ' Bell
Const ASC_BACKSPACE:Int = 8 ' Backspace
Const ASC_TAB:Int = 9       ' Horizontal tab
Const ASC_LF:Int = 10       ' Linefeed
Const ASC_VTAB:Int = 11     ' Vertical tab
Const ASC_FF:Int = 12       ' Form feed
Const ASC_CR:Int = 13       ' Carriage return
Const ASC_SO:Int = 14       ' Shift Out
Const ASC_SI:Int = 15       ' Shift In
Const ASC_DLE:Int = 16      ' Data Line Escape
Const ASC_DC1:Int = 17      ' Device Control 1
Const ASC_DC2:Int = 18      ' Device Control 2
Const ASC_DC3:Int = 19      ' Device Control 3
Const ASC_DC4:Int = 20      ' Device Control 4
Const ASC_NAK:Int = 21      ' Negative Acknowledgment
Const ASC_SYN:Int = 22      ' Synchronous Idle
Const ASC_ETB:Int = 23      ' End of Transmit Block
Const ASC_CAN:Int = 24      ' Cancel
Const ASC_EM:Int = 25       ' End of Medium
Const ASC_SUB:Int = 26      ' Substitute
Const ASC_ESCAPE:Int = 27   ' Escape
Const ASC_FS:Int = 28       ' File separator
Const ASC_GS:Int = 29       ' Group separator
Const ASC_RS:Int = 30       ' Record separator
Const ASC_US:Int = 31       ' Unit separator

' visible characters
Const ASC_SPACE:Int = 32                ' '
Const ASC_EXCLAMATION:Int = 33          '!'
Const ASC_DOUBLE_QUOTE:Int = 34         '"'
Const ASC_HASH:Int = 35                 '#'
Const ASC_DOLLAR:Int = 36               '$'
Const ASC_PERCENT:Int = 37              '%'
Const ASC_AMPERSAND:Int = 38            '&'
Const ASC_SINGLE_QUOTE:Int = 39         '''
Const ASC_OPEN_PARENTHESIS:Int = 40     '('
Const ASC_CLOSE_PARENTHESIS:Int = 41    ')'
Const ASC_ASTERISK:Int = 42             '*'
Const ASC_PLUS:Int = 43                 '+'
Const ASC_COMMA:Int = 44                ','
Const ASC_HYPHEN:Int = 45               '-'
Const ASC_PERIOD:Int = 46               '.'
Const ASC_SLASH:Int = 47                '/'
Const ASC_0:Int = 48
Const ASC_1:Int = 49
Const ASC_2:Int = 50
Const ASC_3:Int = 51
Const ASC_4:Int = 52
Const ASC_5:Int = 53
Const ASC_6:Int = 54
Const ASC_7:Int = 55
Const ASC_8:Int = 56
Const ASC_9:Int = 57
Const ASC_COLON:Int = 58        ':'
Const ASC_SEMICOLON:Int = 59    ';'
Const ASC_LESS_THAN:Int = 60    '<'
Const ASC_EQUALS:Int = 61       '='
Const ASC_GREATER_THAN:Int = 62 '>'
Const ASC_QUESTION:Int = 63     '?'
Const ASC_AT:Int = 64           '@'
Const ASC_UPPER_A:Int = 65
Const ASC_UPPER_B:Int = 66
Const ASC_UPPER_C:Int = 67
Const ASC_UPPER_D:Int = 68
Const ASC_UPPER_E:Int = 69
Const ASC_UPPER_F:Int = 70
Const ASC_UPPER_G:Int = 71
Const ASC_UPPER_H:Int = 72
Const ASC_UPPER_I:Int = 73
Const ASC_UPPER_J:Int = 74
Const ASC_UPPER_K:Int = 75
Const ASC_UPPER_L:Int = 76
Const ASC_UPPER_M:Int = 77
Const ASC_UPPER_N:Int = 78
Const ASC_UPPER_O:Int = 79
Const ASC_UPPER_P:Int = 80
Const ASC_UPPER_Q:Int = 81
Const ASC_UPPER_R:Int = 82
Const ASC_UPPER_S:Int = 83
Const ASC_UPPER_T:Int = 84
Const ASC_UPPER_U:Int = 85
Const ASC_UPPER_V:Int = 86
Const ASC_UPPER_W:Int = 87
Const ASC_UPPER_X:Int = 88
Const ASC_UPPER_Y:Int = 89
Const ASC_UPPER_Z:Int = 90
Const ASC_OPEN_BRACKET:Int = 91     '['
Const ASC_BACKSLASH:Int = 92        '\'
Const ASC_CLOSE_BRACKET:Int = 93    ']'
Const ASC_CIRCUMFLEX:Int = 94       '^'
Const ASC_UNDERSCORE:Int = 95       '_'
Const ASC_BACKTICK:Int = 96         '`'
Const ASC_LOWER_A:Int = 97
Const ASC_LOWER_B:Int = 98
Const ASC_LOWER_C:Int = 99
Const ASC_LOWER_D:Int = 100
Const ASC_LOWER_E:Int = 101
Const ASC_LOWER_F:Int = 102
Const ASC_LOWER_G:Int = 103
Const ASC_LOWER_H:Int = 104
Const ASC_LOWER_I:Int = 105
Const ASC_LOWER_J:Int = 106
Const ASC_LOWER_K:Int = 107
Const ASC_LOWER_L:Int = 108
Const ASC_LOWER_M:Int = 109
Const ASC_LOWER_N:Int = 110
Const ASC_LOWER_O:Int = 111
Const ASC_LOWER_P:Int = 112
Const ASC_LOWER_Q:Int = 113
Const ASC_LOWER_R:Int = 114
Const ASC_LOWER_S:Int = 115
Const ASC_LOWER_T:Int = 116
Const ASC_LOWER_U:Int = 117
Const ASC_LOWER_V:Int = 118
Const ASC_LOWER_W:Int = 119
Const ASC_LOWER_X:Int = 120
Const ASC_LOWER_Y:Int = 121
Const ASC_LOWER_Z:Int = 122
Const ASC_OPEN_BRACE:Int = 123  '{'
Const ASC_PIPE:Int = 124        '|'
Const ASC_CLOSE_BRACE:Int = 125 '}'
Const ASC_TILDE:Int = 126       '~'
Const ASC_DELETE:Int = 127

#if HOST="macos" And TARGET="glfw"
	Import "native/diddy.${TARGET}.mac.${LANG}"
#else
	Import "native/diddy.${TARGET}.${LANG}"
#end
Import mojo
Import assert

Extern

	#If LANG="cpp" Then
		Function RealMillisecs:Int() = "diddy::systemMillisecs"
		Function FlushKeys:Void() = "diddy::flushKeys"
		Function HideMouse:Void() = "diddy::hideMouse"
		Function ShowMouse:Void() = "diddy::showMouse"
		Function GetUpdateRate:Int() = "diddy::getUpdateRate"
		Function SetMouse:Void(x:Int, y:Int) = "diddy::setMouse"
		Function ShowKeyboard:Void() = "diddy::showKeyboard"
		Function LaunchBrowser:Void(address:String) = "diddy::launchBrowser"
		Function LaunchEmail:Void(email:String, subject:String, text:String) = "diddy::launchEmail"
	#Else
		Function RealMillisecs:Int() = "diddy.systemMillisecs"
		Function FlushKeys:Void() = "diddy.flushKeys"
		Function HideMouse:Void() = "diddy.hideMouse"
		Function ShowMouse:Void() = "diddy.showMouse"
		Function GetUpdateRate:Int() = "diddy.getUpdateRate"
		Function SetMouse:Void(x:Int, y:Int) = "diddy.setMouse"
		Function ShowKeyboard:Void() = "diddy.showKeyboard"
		Function LaunchBrowser:Void(address:String) = "diddy.launchBrowser"
		Function LaunchEmail:Void(email:String, subject:String, text:String) = "diddy.launchEmail"
		Function SetGraphics:Void(w:Int, h:Int) = "diddy.setGraphics"
	#End
Public

Function ExitApp:Void()
	Error ""
End

Function RectsOverlap:Int(x0:Float, y0:Float, w0:Float, h0:Float, x2:Float, y2:Float, w2:Float, h2:Float)
	If x0 > (x2 + w2) Or (x0 + w0) < x2 Then Return False
	If y0 > (y2 + h2) Or (y0 + h0) < y2 Then Return False
	Return True
End

Function DrawRectOutline:Void(x:Int, y:Int, w:Int, h:Int)
	w -= 1
	h -= 1
	DrawLine(x,y,x+w,y)
	DrawLine(x+w,y,x+w,y+h)
	DrawLine(x+w,y+h,x,y+h)
	DrawLine(x,y+h,x,y)
End

Function LoadBitmap:Image(path$, flags%=0)
	Local pointer:Image = LoadImage(path, 1, flags)

	AssertNotNull(pointer, "Error loading bitmap "+path)

   	Return pointer
End

Function LoadAnimBitmap:Image(path$, w%, h%, count%, tmpImage:Image)
	'tmpImage = loadBitmap(path) <-- This creates another image, decided to just copy the code here
	tmpImage = LoadImage(path)

	AssertNotNull(tmpImage, "Error loading bitmap "+path)

	Local pointer:Image = tmpImage.GrabImage( 0, 0, w, h, count, Image.MidHandle)

   	Return pointer
End

Function LoadSoundSample:Sound(path$)
	Local pointer:Sound = LoadSound(path)
	AssertNotNull(pointer, "Error loading sound "+path)
	Return pointer
End

Function FormatNumeric:String(value:Float)
	Local i:Int,s:String,ns:String,k:Int
	Local os:String
	s=String(value)
	os=s
	Local pos:Int=s.Length()
	If s.Find(".")>0 pos=s.Find(".") Else os=""
	For i=pos To 1 Step -1
		If k>2 ns+="." k=0
		k+=1
		ns=ns+Mid(s,i,1)
	Next
	s=""
	For i= ns.Length() To 1 Step -1
		s+=Mid(ns,i,1)
	Next
	If os<>"" s=s+","+os[pos+1..]
	Return s
End

Function Left$( str$,n:Int )
	If n>str.Length() n=str.Length()
	Return str[..n]
End

Function Right$( str$,n:Int )
	If n>str.Length() n=str.Length()
	Return str[str.Length()-n..]
End

Function LSet$( str$,n:Int,char:String=" " )
	Local rep:String
	For Local i:Int=1 To n
		rep=rep+char
	Next
	str=str+rep
	Return str[..n]
End

Function RSet$( str$,n:Int,char:String=" " )
	Local rep:String
	For Local i:Int=1 To n
		rep=rep+char
	Next
	str=rep+str
	Return str[str.Length()-n..]
End

Function Mid$( str$,pos:Int,size:Int=-1 )
	If pos>str.Length() Return ""
	pos-=1
	If( size<0 ) Return str[pos..]
	If pos<0 size=size+pos pos=0
	If pos+size>str.Length() size=str.Length()-pos
	Return str[pos..pos+size]
End

Function StripDir$( path$ )
	Local i:=path.FindLast( "/" )
	If i<>-1 Return path[i+1..]
	Return path
End

Function StripExt$( path$ )
	Local i:=path.FindLast( "." )
	If i<>-1 And path.Find( "/",i+1 )=-1 Return path[..i]
	Return path
End

Function StripAll$( path$ )
	Return StripDir( StripExt( path ) )
End

Function Round%(flot#)
	Return Floor(flot+0.5)
End

Function PointInSpot:Int(x1:Float, y1:Float, x2:Float, y2:Float, radius:Float)
	Local dx:Float = x2 - x1
	Local dy:Float = y2 - y1
	Return dx * dx + dy * dy <= radius * radius
End

Function AnyInputPressed:Bool()
	For Local i:Int = 0 To 511
		If KeyHit(i) Then Return True
	Next
	Return False
End

Function FormatNumber:String(number:Float, decimal:Int=4, comma:Int=0, padleft:Int=0 )
	Assert(decimal > -1 And comma > -1 And padleft > -1, "Negative numbers not allowed in FormatNumber()")

	Local str:String = number
	Local dl:Int = str.Find(".")
	If decimal = 0 Then decimal = -1
	str = str[..dl+decimal+1]

	If comma
		While dl>comma
			str = str[..dl-comma] + "," + str[dl-comma..]
			dl -= comma
		Wend
	End

	If padleft
		Local paddedLength:Int = padleft+decimal+1
		If paddedLength < str.Length Then str = "Error"
		str = RSet(str,paddedLength)
	End
	Return str
End

Function IsWhitespace:Bool(str:String)
	Return str = "~t" Or str = "~n" Or str = "~r" Or str = " "
End

Function IsWhitespace:Bool(val:Int)
	Return val = ASC_TAB Or val = ASC_LF Or val = ASC_CR Or val = ASC_SPACE
End

' TODO: allow encoding of Int[]
' TODO: enabling lineWrap will wrap the string every lineWrapWidth characters (defaults to 80)
' enabling padOutput will pad the ending of the output with equals characters (=) to next 4-byte boundary
Function EncodeBase64:String(src:String, padOutput:Bool=False, lineWrap:Bool=False, lineWrapWidth:Int=80)
	Local rv:String = ""
	Local s1:Int, s2:Int, s3:Int, a:Int, b:Int, c:Int, d:Int, i:Int
	If src.Length = 0 Then Return ""
	Repeat
		s1 = src[i]
		If i+1 < src.Length Then s2 = src[i+1] Else s2 = 0
		If i+2 < src.Length Then s3 = src[i+2] Else s3 = 0
		a = s1 Shr 2
		b = ((s1 & 3) Shl 4) | (s2 Shr 4)
		c = ((s2 & 15) Shl 2) | (s3 Shr 6)
		d = s3 & 63
		If i+1 >= src.Length Then c = 64
		If i+2 >= src.Length Then d = 64
		rv += BASE64_CHARS[a..a+1] + BASE64_CHARS[b..b+1]
		If c < 64 Or padOutput Then rv += BASE64_CHARS[c..c+1]
		If d < 64 Or padOutput Then rv += BASE64_CHARS[d..d+1]
		i += 3
	Until i >= src.Length
	Return rv
End

' TODO: convert this to use BASE64_ARRAY
' I'll do this once Monkey has a decent string builder class.
Function DecodeBase64:String(src:String)
	Local rv:String = "", src2:String = ""
	Local s1:Int, s2:Int, s3:Int, a:Int, b:Int, c:Int, d:Int, i:Int
	' remove any non-base64 characters
	For i = 0 Until src.Length
		If BASE64_CHARS.Find(src[i..i+1]) >= 0 Then src2 += src[i..i+1]
	Next
	If src2.Length = 0 Then Return ""
	i = 0
	Repeat
		a = BASE64_CHARS.Find(src2[i..i+1])
		If i+1 > src2.Length Then Exit ' This shouldn't happen with base64, so something's wrong!
		b = BASE64_CHARS.Find(src2[i+1..i+2])
		If i+2 < src2.Length Then c = BASE64_CHARS.Find(src2[i+2..i+3]) Else c = 64
		If i+3 < src2.Length Then d = BASE64_CHARS.Find(src2[i+3..i+4]) Else d = 64
		s1 = (a Shl 2) | (b Shr 4)
		s2 = ((b & 15) Shl 4) | (c Shr 2)
		s3 = ((c & 3) Shl 6) | d
		rv += String.FromChar(s1)
		If c <> 64 Then rv += String.FromChar(s2)
		If d <> 64 Then rv += String.FromChar(s3)
		i += 4
	Until i >= src2.Length
	Return rv
End

Function DecodeBase64Bytes:Int[](src:String)
	InitBase64()
	Local a:Int, b:Int, c:Int, d:Int, i:Int, j:Int
	Local src2:Int[] = New Int[src.Length]

	' find out how many base64 characters
	Local srclen:Int = 0
	For i = 0 Until src.Length
		If BASE64_ARRAY[src[i]] >= 0 Then
			src2[srclen] = src[i]
			srclen += 1
		End
	Next

	' die if there are no base64 chars
	If srclen = 0 Then Return []

	' get the target length and create the array
	Local len:Int = 3*srclen/4
	If srclen Mod 4 = 1 Or srclen Mod 4 = 2 Then
		len += 1
	ElseIf srclen Mod 4 = 3 Then
		len += 2
	End
	Local rv:Int[] = New Int[len]

	i = 0
	j = 0
	Repeat
		a = BASE64_ARRAY[src2[i]]
		If i+1 > srclen Then Exit ' This shouldn't happen with base64, so something's wrong!
		b = BASE64_ARRAY[src2[i+1]]
		If i+2 < srclen Then c = BASE64_ARRAY[src2[i+2]] Else c = 64
		If i+3 < srclen Then d = BASE64_ARRAY[src2[i+3]] Else d = 64
		rv[j] = (a Shl 2) | (b Shr 4)
		If j+1 < len Then rv[j+1] = ((b & 15) Shl 4) | (c Shr 2)
		If j+2 < len Then rv[j+2] = ((c & 3) Shl 6) | d
		i += 4
		j += 3
	Until i >= srclen
	Return rv
End

Function InitBase64:Void()
	If BASE64_ARRAY.Length = 0 Then
		BASE64_ARRAY = New Int[256]
		Local i% = 0
		For i = 0 Until BASE64_ARRAY.Length
			BASE64_ARRAY[i] = -1
		Next
		For i = 0 Until BASE64_CHARS.Length
			BASE64_ARRAY[BASE64_CHARS[i]] = i
		Next
	End
End

' constants

Const BASE64_CHARS:String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
Global BASE64_ARRAY:Int[] = []

' control characters
Const ASC_NUL:Int = 0       ' Null character
Const ASC_SOH:Int = 1       ' Start of Heading
Const ASC_STX:Int = 2       ' Start of Text
Const ASC_ETX:Int = 3       ' End of Text
Const ASC_EOT:Int = 4       ' End of Transmission
Const ASC_ENQ:Int = 5       ' Enquiry
Const ASC_ACK:Int = 6       ' Acknowledgment
Const ASC_BEL:Int = 7       ' Bell
Const ASC_BACKSPACE:Int = 8 ' Backspace
Const ASC_TAB:Int = 9       ' Horizontal tab
Const ASC_LF:Int = 10       ' Linefeed
Const ASC_VTAB:Int = 11     ' Vertical tab
Const ASC_FF:Int = 12       ' Form feed
Const ASC_CR:Int = 13       ' Carriage return
Const ASC_SO:Int = 14       ' Shift Out
Const ASC_SI:Int = 15       ' Shift In
Const ASC_DLE:Int = 16      ' Data Line Escape
Const ASC_DC1:Int = 17      ' Device Control 1
Const ASC_DC2:Int = 18      ' Device Control 2
Const ASC_DC3:Int = 19      ' Device Control 3
Const ASC_DC4:Int = 20      ' Device Control 4
Const ASC_NAK:Int = 21      ' Negative Acknowledgment
Const ASC_SYN:Int = 22      ' Synchronous Idle
Const ASC_ETB:Int = 23      ' End of Transmit Block
Const ASC_CAN:Int = 24      ' Cancel
Const ASC_EM:Int = 25       ' End of Medium
Const ASC_SUB:Int = 26      ' Substitute
Const ASC_ESCAPE:Int = 27   ' Escape
Const ASC_FS:Int = 28       ' File separator
Const ASC_GS:Int = 29       ' Group separator
Const ASC_RS:Int = 30       ' Record separator
Const ASC_US:Int = 31       ' Unit separator

' visible characters
Const ASC_SPACE:Int = 32                ' '
Const ASC_EXCLAMATION:Int = 33          '!'
Const ASC_DOUBLE_QUOTE:Int = 34         '"'
Const ASC_HASH:Int = 35                 '#'
Const ASC_DOLLAR:Int = 36               '$'
Const ASC_PERCENT:Int = 37              '%'
Const ASC_AMPERSAND:Int = 38            '&'
Const ASC_SINGLE_QUOTE:Int = 39         '''
Const ASC_OPEN_PARENTHESIS:Int = 40     '('
Const ASC_CLOSE_PARENTHESIS:Int = 41    ')'
Const ASC_ASTERISK:Int = 42             '*'
Const ASC_PLUS:Int = 43                 '+'
Const ASC_COMMA:Int = 44                ','
Const ASC_HYPHEN:Int = 45               '-'
Const ASC_PERIOD:Int = 46               '.'
Const ASC_SLASH:Int = 47                '/'
Const ASC_0:Int = 48
Const ASC_1:Int = 49
Const ASC_2:Int = 50
Const ASC_3:Int = 51
Const ASC_4:Int = 52
Const ASC_5:Int = 53
Const ASC_6:Int = 54
Const ASC_7:Int = 55
Const ASC_8:Int = 56
Const ASC_9:Int = 57
Const ASC_COLON:Int = 58        ':'
Const ASC_SEMICOLON:Int = 59    ';'
Const ASC_LESS_THAN:Int = 60    '<'
Const ASC_EQUALS:Int = 61       '='
Const ASC_GREATER_THAN:Int = 62 '>'
Const ASC_QUESTION:Int = 63     '?'
Const ASC_AT:Int = 64           '@'
Const ASC_UPPER_A:Int = 65
Const ASC_UPPER_B:Int = 66
Const ASC_UPPER_C:Int = 67
Const ASC_UPPER_D:Int = 68
Const ASC_UPPER_E:Int = 69
Const ASC_UPPER_F:Int = 70
Const ASC_UPPER_G:Int = 71
Const ASC_UPPER_H:Int = 72
Const ASC_UPPER_I:Int = 73
Const ASC_UPPER_J:Int = 74
Const ASC_UPPER_K:Int = 75
Const ASC_UPPER_L:Int = 76
Const ASC_UPPER_M:Int = 77
Const ASC_UPPER_N:Int = 78
Const ASC_UPPER_O:Int = 79
Const ASC_UPPER_P:Int = 80
Const ASC_UPPER_Q:Int = 81
Const ASC_UPPER_R:Int = 82
Const ASC_UPPER_S:Int = 83
Const ASC_UPPER_T:Int = 84
Const ASC_UPPER_U:Int = 85
Const ASC_UPPER_V:Int = 86
Const ASC_UPPER_W:Int = 87
Const ASC_UPPER_X:Int = 88
Const ASC_UPPER_Y:Int = 89
Const ASC_UPPER_Z:Int = 90
Const ASC_OPEN_BRACKET:Int = 91     '['
Const ASC_BACKSLASH:Int = 92        '\'
Const ASC_CLOSE_BRACKET:Int = 93    ']'
Const ASC_CIRCUMFLEX:Int = 94       '^'
Const ASC_UNDERSCORE:Int = 95       '_'
Const ASC_BACKTICK:Int = 96         '`'
Const ASC_LOWER_A:Int = 97
Const ASC_LOWER_B:Int = 98
Const ASC_LOWER_C:Int = 99
Const ASC_LOWER_D:Int = 100
Const ASC_LOWER_E:Int = 101
Const ASC_LOWER_F:Int = 102
Const ASC_LOWER_G:Int = 103
Const ASC_LOWER_H:Int = 104
Const ASC_LOWER_I:Int = 105
Const ASC_LOWER_J:Int = 106
Const ASC_LOWER_K:Int = 107
Const ASC_LOWER_L:Int = 108
Const ASC_LOWER_M:Int = 109
Const ASC_LOWER_N:Int = 110
Const ASC_LOWER_O:Int = 111
Const ASC_LOWER_P:Int = 112
Const ASC_LOWER_Q:Int = 113
Const ASC_LOWER_R:Int = 114
Const ASC_LOWER_S:Int = 115
Const ASC_LOWER_T:Int = 116
Const ASC_LOWER_U:Int = 117
Const ASC_LOWER_V:Int = 118
Const ASC_LOWER_W:Int = 119
Const ASC_LOWER_X:Int = 120
Const ASC_LOWER_Y:Int = 121
Const ASC_LOWER_Z:Int = 122
Const ASC_OPEN_BRACE:Int = 123  '{'
Const ASC_PIPE:Int = 124        '|'
Const ASC_CLOSE_BRACE:Int = 125 '}'
Const ASC_TILDE:Int = 126       '~'
Const ASC_DELETE:Int = 127


Added tile to main import.

Added tile to main import.

'
' Module diddy.diddy
'
Import mojo
Import functions
Import framework
Import inputcache
Import tween
Import collections
Import assert
Import gui
Import serialization
Import xml
Import io
Import tile


Fixed up a preprocessor check for the Windows 8/RT targets.

Strict

Public

' Preprocessor related:
#SIZEOF_IMPLEMENTED = True

' By enabling this, you allow 'SizeOf' to be used with the standard 'DataBuffer' class.
#SIZEOF_DATABUFFER = True

#If LANG = "cpp"
	#SIZEOF_NATIVE = True
#End

#If LANG = "cpp" And TARGET <> "win8" And TARGET <> "winrt"
	#SIZEOF_NORMAL_CPP_TARGET = True
#End

' Imports (Public):
'Import vector

#If SIZEOF_NATIVE
	Import "native/sizes.${LANG}"
#End

' Imports (Private):
Private

#If SIZEOF_DATABUFFER
	Import brl.databuffer
#End

Public

' Global & Constant variable(s):

' Standard type sizes:
Const SizeOf_Octet:Int = 1
Const SizeOf_Octet_InBits:Int = 8

Const SizeOf_Byte:= SizeOf_Octet
Const SizeOf_Byte_InBits:= SizeOf_Octet_InBits

#If Not SIZEOF_NATIVE
	Const SizeOf_Integer:= 4 * SizeOf_Octet ' 8

	#If CPP_DOUBLE_PRECISION_FLOATS And SIZEOF_NORMAL_CPP_TARGET
		Const SizeOf_FloatingPoint:= 8 * SizeOf_Octet
	#Else
		Const SizeOf_FloatingPoint:= 4 * SizeOf_Octet
	#End

	Const SizeOf_Boolean:= SizeOf_Byte ' 1

	Const SizeOf_Short:= 2 * SizeOf_Octet
	Const SizeOf_Long:= 8 * SizeOf_Octet
#Else
	' Global variable(s) (External):

	' Modifying these values is considered non-standard, and should not be attempted:
	Extern

	Global SizeOf_Integer:Int = "sizeof_int"
	Global SizeOf_FloatingPoint:Int = "sizeof_Float"
	Global SizeOf_Boolean:Int = "sizeof_boolean"

	Global SizeOf_Short:Int = "sizeof_short"
	Global SizeOf_Long:Int = "sizeof_long"

	Public
#End

#If SIZEOF_NATIVE And LANG = "cpp" And SIZEOF_NORMAL_CPP_TARGET
	' Global variable(s) (External):

	Extern

	' Modifying the value of this variable is considered non-standard, and should not be attempted.
	Global SizeOf_Char:Int = "sizeof_Char"

	Public
#Else
	Const SizeOf_Char:= SizeOf_Byte ' 1
#End

' Type sizes (In bits):
Global SizeOf_Char_InBits:= SizeOf_Char * SizeOf_Octet_InBits
Global SizeOf_Integer_InBits:= SizeOf_Integer * SizeOf_Octet_InBits
Global SizeOf_FloatingPoint_InBits:= SizeOf_FloatingPoint * SizeOf_Octet_InBits
Global SizeOf_Boolean_InBits:= SizeOf_Boolean * SizeOf_Byte_InBits ' SizeOf_Octet_InBits

Global SizeOf_Short_InBits:= SizeOf_Short * SizeOf_Octet_InBits
Global SizeOf_Long_InBits:= SizeOf_Long * SizeOf_Octet_InBits

' External bindings:
#If SIZEOF_NATIVE
	Extern

	#If LANG = "cpp"
		' Functions (Native):
		Function SizeOf:Int(I:Int)="(int)sizeof"
		Function SizeOf:Int(F:Float)="(int)sizeof"
		Function SizeOf:Int(B:Bool)="(int)sizeof"
	#Else
		' Nothing so far.
	#End

	Public
#End

' Functions (Monkey):

' This is mainly used for internal stream functionality in 'util'.
Function IntSize:Int(Size:Int)
	If (Size > 1) Then
		If (Size > 4) Then
			Return SizeOf_Long
		Elseif (Size < 4) Then
			Return SizeOf_Short
		Else
			Return SizeOf_Integer
		Endif
	Endif

	' Return the default response.
	Return SizeOf_Byte
End

#If Not SIZEOF_NATIVE
	Function SizeOf:Int(I:Int)
		Return SizeOf_Integer
	End

	Function SizeOf:Int(F:Float)
		Return SizeOf_FloatingPoint
	End

	Function SizeOf:Int(B:Bool)
		Return SizeOf_Boolean
	End
#End

#If Not MONKEYLANG_EXPLICIT_BOXES
	Function SizeOf:Int(IO:IntObject)
		Return SizeOf(IO.ToInt())
	End

	Function SizeOf:Int(FO:FloatObject)
		Return SizeOf(FO.ToFloat())
	End

	Function SizeOf:Int(BO:BoolObject)
		Return SizeOf(BO.ToBool())
	End

	Function SizeOf:Int(S:StringObject, IsString:Bool=True)
		Return SizeOf(S.ToString(), IsString)
	End
#End

Function SizeOf:Int(S:String, IsString:Bool=True)
	If (Not IsString) Then
		' Make the input-string upper-case.
		S = S.ToUpper()

		If (S.Find("INT") <> -1) Then
			Return SizeOf(0)
		Elseif (S.Find("FLOAT") <> -1) Then
			Return SizeOf(0.0)
		Elseif (S.Find("BOOL") <> -1) Then
			Return SizeOf(False)
		#Rem
			Elseif (S.Find("VEC") <> -1) Then
				Return Max(Int(S), 1)
		#End

		#Rem
			Elseif (S.Find("BYTE")) Then
				Return SizeOf_Byte
		#End
		Endif
	Endif

	' Return the default response.
	Return SizeOf_Char * S.Length()
End

#If SIZEOF_DATABUFFER
	Function SizeOf:Int(Data:DataBuffer)
		Return Data.Length()
	End
#End

Function InBits:Int(I:Int)
	Return SizeOf_Integer_InBits
End

Function InBits:Int(F:Float)
	Return SizeOf_FloatingPoint_InBits
End

Function InBits:Int(B:Bool)
	Return SizeOf_Boolean_InBits
End

Function InBits:Int(S:String, IsString:Bool=False)
	Local Output:= SizeOf(S, IsString)

	If (Not IsString) Then
		Return Output * SizeOf_Octet_InBits
	Endif

	Return Output * SizeOf_Char_InBits
End

Strict

Public

' Preprocessor related:
#SIZEOF_IMPLEMENTED = True

' By enabling this, you allow 'SizeOf' to be used with the standard 'DataBuffer' class.
#SIZEOF_DATABUFFER = True

#If LANG = "cpp"
	#SIZEOF_NATIVE = True
#End

#If LANG = "cpp" And TARGET <> "win8"
	#SIZEOF_NORMAL_CPP_TARGET = True
#End

' Imports (Public):
'Import vector

#If SIZEOF_NATIVE
	Import "native/sizes.${LANG}"
#End

' Imports (Private):
Private

#If SIZEOF_DATABUFFER
	Import brl.databuffer
#End

Public

' Global & Constant variable(s):

' Standard type sizes:
Const SizeOf_Octet:Int = 1
Const SizeOf_Octet_InBits:Int = 8

Const SizeOf_Byte:= SizeOf_Octet
Const SizeOf_Byte_InBits:= SizeOf_Octet_InBits

#If Not SIZEOF_NATIVE
	Const SizeOf_Integer:= 4 * SizeOf_Octet ' 8

	#If CPP_DOUBLE_PRECISION_FLOATS And SIZEOF_NORMAL_CPP_TARGET
		Const SizeOf_FloatingPoint:= 8 * SizeOf_Octet
	#Else
		Const SizeOf_FloatingPoint:= 4 * SizeOf_Octet
	#End

	Const SizeOf_Boolean:= SizeOf_Byte ' 1

	Const SizeOf_Short:= 2 * SizeOf_Octet
	Const SizeOf_Long:= 8 * SizeOf_Octet
#Else
	' Global variable(s) (External):

	' Modifying these values is considered non-standard, and should not be attempted:
	Extern

	Global SizeOf_Integer:Int = "sizeof_int"
	Global SizeOf_FloatingPoint:Int = "sizeof_Float"
	Global SizeOf_Boolean:Int = "sizeof_boolean"

	Global SizeOf_Short:Int = "sizeof_short"
	Global SizeOf_Long:Int = "sizeof_long"

	Public
#End

#If SIZEOF_NATIVE And LANG = "cpp" And SIZEOF_NORMAL_CPP_TARGET
	' Global variable(s) (External):

	Extern

	' Modifying the value of this variable is considered non-standard, and should not be attempted.
	Global SizeOf_Char:Int = "sizeof_Char"

	Public
#Else
	Const SizeOf_Char:= SizeOf_Byte ' 1
#End

' Type sizes (In bits):
Global SizeOf_Char_InBits:= SizeOf_Char * SizeOf_Octet_InBits
Global SizeOf_Integer_InBits:= SizeOf_Integer * SizeOf_Octet_InBits
Global SizeOf_FloatingPoint_InBits:= SizeOf_FloatingPoint * SizeOf_Octet_InBits
Global SizeOf_Boolean_InBits:= SizeOf_Boolean * SizeOf_Byte_InBits ' SizeOf_Octet_InBits

Global SizeOf_Short_InBits:= SizeOf_Short * SizeOf_Octet_InBits
Global SizeOf_Long_InBits:= SizeOf_Long * SizeOf_Octet_InBits

' External bindings:
#If SIZEOF_NATIVE
	Extern

	#If LANG = "cpp"
		' Functions (Native):
		Function SizeOf:Int(I:Int)="(int)sizeof"
		Function SizeOf:Int(F:Float)="(int)sizeof"
		Function SizeOf:Int(B:Bool)="(int)sizeof"
	#Else
		' Nothing so far.
	#End

	Public
#End

' Functions (Monkey):

' This is mainly used for internal stream functionality in 'util'.
Function IntSize:Int(Size:Int)
	If (Size > 1) Then
		If (Size > 4) Then
			Return SizeOf_Long
		Elseif (Size < 4) Then
			Return SizeOf_Short
		Else
			Return SizeOf_Integer
		Endif
	Endif

	' Return the default response.
	Return SizeOf_Byte
End

#If Not SIZEOF_NATIVE
	Function SizeOf:Int(I:Int)
		Return SizeOf_Integer
	End

	Function SizeOf:Int(F:Float)
		Return SizeOf_FloatingPoint
	End

	Function SizeOf:Int(B:Bool)
		Return SizeOf_Boolean
	End
#End

#If Not MONKEYLANG_EXPLICIT_BOXES
	Function SizeOf:Int(IO:IntObject)
		Return SizeOf(IO.ToInt())
	End

	Function SizeOf:Int(FO:FloatObject)
		Return SizeOf(FO.ToFloat())
	End

	Function SizeOf:Int(BO:BoolObject)
		Return SizeOf(BO.ToBool())
	End

	Function SizeOf:Int(S:StringObject, IsString:Bool=True)
		Return SizeOf(S.ToString(), IsString)
	End
#End

Function SizeOf:Int(S:String, IsString:Bool=True)
	If (Not IsString) Then
		' Make the input-string upper-case.
		S = S.ToUpper()

		If (S.Find("INT") <> -1) Then
			Return SizeOf(0)
		Elseif (S.Find("FLOAT") <> -1) Then
			Return SizeOf(0.0)
		Elseif (S.Find("BOOL") <> -1) Then
			Return SizeOf(False)
		#Rem
			Elseif (S.Find("VEC") <> -1) Then
				Return Max(Int(S), 1)
		#End

		#Rem
			Elseif (S.Find("BYTE")) Then
				Return SizeOf_Byte
		#End
		Endif
	Endif

	' Return the default response.
	Return SizeOf_Char * S.Length()
End

#If SIZEOF_DATABUFFER
	Function SizeOf:Int(Data:DataBuffer)
		Return Data.Length()
	End
#End

Function InBits:Int(I:Int)
	Return SizeOf_Integer_InBits
End

Function InBits:Int(F:Float)
	Return SizeOf_FloatingPoint_InBits
End

Function InBits:Int(B:Bool)
	Return SizeOf_Boolean_InBits
End

Function InBits:Int(S:String, IsString:Bool=False)
	Local Output:= SizeOf(S, IsString)

	If (Not IsString) Then
		Return Output * SizeOf_Octet_InBits
	Endif

	Return Output * SizeOf_Char_InBits
End


- Added strip mangling to dll functions on non-Windows platforms.

Strict

Private
#If LANG="cpp"
Import "native/lib.cpp"
#Else
#Error "Only the C++ language is supported in Lib module"
#End

Extern Private
Function _LoadLib:Int(libname:String) = "LoadLib"
Function _LibFunction:Int(lib:Int, funcname:String) = "LibFunction"

Extern
Function FreeLib:Void(lib:Int)
Function PushLibInt:Void(val:Int)
Function PushLibFloat:Void(val:Float)
Function PushLibString:Void(str:String)
'Function PushLibObject:Void(obj:Object)
Function CallVoidFunction:Void(func:Int)
Function CallIntFunction:Int(func:Int)
Function CallFloatFunction:Float(func:Int)
Function CallStringFunction:String(func:Int)
'Function CallObjectFunction:Int(func:Int)
Function CallVoidCFunction:Void(func:Int)
Function CallIntCFunction:Int(func:Int)
Function CallFloatCFunction:Float(func:Int)
Function CallStringCFunction:String(func:Int)
'Function CallObjectCFunction:Object(func:Int)

Public

Function LoadLib:Int(libname:String)
#If HOST="winnt"
	libname += ".dll"
#Elseif HOST="macos"
	libname += ".dylib"
#Elseif HOST="linux"
	libname += ".so"
#End
	Return _LoadLib(libname)
End

Function LibFunction:Int(lib:Int, funcname:String)
#If HOST="macos" Or HOST="linux"
	funcname = funcname.Split("@")[0]
	If String.FromChar(funcname[0]) = "_" Then funcname = funcname[1 ..]
#End
	Return _LibFunction(lib, funcname)
End

Strict

Private
#If LANG="cpp"
Import "native/lib.cpp"
#Else
#Error "Only the C++ language is supported in Lib module"
#End

Extern Private
Function _LoadLib:Int(libname:String) = "LoadLib"

Extern
Function FreeLib:Void(lib:Int)
Function LibFunction:Int(lib:Int, funcname:String)
Function PushLibInt:Void(val:Int)
Function PushLibFloat:Void(val:Float)
Function PushLibString:Void(str:String)
'Function PushLibObject:Void(obj:Object)
Function CallVoidFunction:Void(func:Int)
Function CallIntFunction:Int(func:Int)
Function CallFloatFunction:Float(func:Int)
Function CallStringFunction:String(func:Int)
'Function CallObjectFunction:Int(func:Int)
Function CallVoidCFunction:Void(func:Int)
Function CallIntCFunction:Int(func:Int)
Function CallFloatCFunction:Float(func:Int)
Function CallStringCFunction:String(func:Int)
'Function CallObjectCFunction:Object(func:Int)

Public

Function LoadLib:Int(libname:String)
#If HOST="winnt"
	libname += ".dll"
#Elseif HOST="macos"
	libname += ".dylib"
#Elseif HOST="linux"
	libname += ".so"
#End
	Return _LoadLib(libname)
End


Setter of Sprite.loopAnimation removed

Setter of Sprite.loopAnimation removed

Strict

Private

Import mojo
Import animationable
Import director
Import vector2d

Public

Class Sprite Implements Animationable
    Private

    Field image:Image
    Field frameTimer:Int
    Field currentFrame:Int
    Field frameCount:Int
    Field frameSpeed:Int
    Field size_:Vector2D
    Field center_:Vector2D

    Public

    Field pos:Vector2D
    Field rotation:Float
    Field scale:Vector2D = New Vector2D(1, 1)
    Field loopAnimation:Bool

    Method size:Vector2D() Property
        Return size_
    End

    Method center:Vector2D() Property
        Return center_
    End

    Method New(imageName:String, pos:Vector2D=Null)
        image = LoadImage(imageName)
        InitVectors(image.Width(), image.Height(), pos)
    End

    Method New(imageName:String, frameWidth:Int, frameHeight:Int, frameCount:Int, frameSpeed:Int, pos:Vector2D=Null)
        Self.frameCount = frameCount - 1
        Self.frameSpeed = frameSpeed

        image = LoadImage(imageName, frameWidth, frameHeight, frameCount)
        InitVectors(frameWidth, frameHeight, pos)
    End

    Method AnimationIsDone:Bool()
        If loopAnimation Then Return False
        Return (currentFrame = frameCount)
    End

    Method OnRender:Void()
        DrawImage(image, pos.x, pos.y, rotation, scale.x, scale.y, currentFrame)
    End

    Method OnUpdate:Void()
        UpdateAnimation()
    End

    Method CenterX:Void()
        pos.x = CurrentDirector().center.x - center_.x
    End

    Method CenterY:Void()
        pos.y = CurrentDirector().center_.y - center_.y
    End

    Method Center:Void()
        pos = CurrentDirector().center_.Copy().Sub(center_)
    End

    Private

    Method UpdateAnimation:Void()
        If frameCount <= 0 Then Return
        If AnimationIsDone() Then Return

        If frameTimer = 0 Then
            frameTimer = Millisecs()
            Return
        End
        If Millisecs() < frameTimer + frameSpeed Then Return

        If currentFrame = frameCount
            If loopAnimation
                currentFrame = 1
            End
        Else
            currentFrame += 1
        End
        frameTimer = Millisecs()
    End

    Method InitVectors:Void(width:Int, height:Int, pos:Vector2D=Null)
        If pos = Null
            Self.pos = New Vector2D(0, 0)
        Else
            Self.pos = pos
        End

        size_ = New Vector2D(width, height)
        center_ = size_.Copy().Div(2)
    End
End

Strict

Private

Import mojo
Import animationable
Import director
Import vector2d

Public

Class Sprite Implements Animationable
    Private

    Field image:Image
    Field frameTimer:Int
    Field currentFrame:Int
    Field frameCount:Int
    Field frameSpeed:Int
    Field loopAnimation:Bool
    Field size_:Vector2D
    Field center_:Vector2D

    Public

    Field pos:Vector2D
    Field rotation:Float
    Field scale:Vector2D = New Vector2D(1, 1)

    Method size:Vector2D() Property
        Return size_
    End

    Method center:Vector2D() Property
        Return center_
    End

    Method New(imageName:String, pos:Vector2D=Null)
        image = LoadImage(imageName)
        InitVectors(image.Width(), image.Height(), pos)
    End

    Method New(imageName:String, frameWidth:Int, frameHeight:Int, frameCount:Int, frameSpeed:Int, pos:Vector2D=Null)
        Self.frameCount = frameCount - 1
        Self.frameSpeed = frameSpeed

        image = LoadImage(imageName, frameWidth, frameHeight, frameCount)
        InitVectors(frameWidth, frameHeight, pos)
    End

    Method LoopAnimation:Void(flag:Bool=True)
        loopAnimation = flag
    End

    Method AnimationIsDone:Bool()
        If loopAnimation Then Return False
        Return (currentFrame = frameCount)
    End

    Method OnRender:Void()
        DrawImage(image, pos.x, pos.y, rotation, scale.x, scale.y, currentFrame)
    End

    Method OnUpdate:Void()
        UpdateAnimation()
    End

    Method CenterX:Void()
        pos.x = CurrentDirector().center.x - center_.x
    End

    Method CenterY:Void()
        pos.y = CurrentDirector().center_.y - center_.y
    End

    Method Center:Void()
        pos = CurrentDirector().center_.Copy().Sub(center_)
    End

    Private

    Method UpdateAnimation:Void()
        If frameCount <= 0 Then Return
        If AnimationIsDone() Then Return

        If frameTimer = 0 Then
            frameTimer = Millisecs()
            Return
        End
        If Millisecs() < frameTimer + frameSpeed Then Return

        If currentFrame = frameCount
            If loopAnimation
                currentFrame = 1
            End
        Else
            currentFrame += 1
        End
        frameTimer = Millisecs()
    End

    Method InitVectors:Void(width:Int, height:Int, pos:Vector2D=Null)
        If pos = Null
            Self.pos = New Vector2D(0, 0)
        Else
            Self.pos = pos
        End

        size_ = New Vector2D(width, height)
        center_ = size_.Copy().Div(2)
    End
End


Total number of tests and headline for the testreport

Strict

Private

Import bono.src.exceptions
Import reflection
Import testlistener
Import testsuite

Public

Class TestListenerSimple Implements TestListener
    Private

    Field lastClass:ClassInfo
    Field currentClass:ClassInfo
    Field currentMethod:MethodInfo
    Field result:Int = PASSED
    Field numTests:Int
    Field failureMessages:StringMap<String> = New StringMap<String>()
    Field skipMessages:StringMap<String> = New StringMap<String>()
    Field incompleteMessages:StringMap<String> = New StringMap<String>()

    Const PASSED:Int = 0
    Const FAILED:Int = 1
    Const SKIPPED:Int = 2
    Const INCOMPLETE:Int = 3

    Public

    Method StartTestSuite:Void(suite:TestSuite)
        Print "Tests within this testsuite:~n"
    End

    Method StartTest:Void(classInfo:ClassInfo, methodInfo:MethodInfo)
        currentClass = classInfo
        currentMethod = methodInfo
        result = PASSED
        numTests += 1
    End

    Method AddFailure:Void(classInfo:ClassInfo, methodInfo:MethodInfo, message:String)
        result = FAILED
        failureMessages.Set(GetFQN(classInfo, methodInfo), message)
    End

    Method AddSkippedTest:Void(classInfo:ClassInfo, methodInfo:MethodInfo, message:String)
        result = SKIPPED
        skipMessages.Set(GetFQN(classInfo, methodInfo), message)
    End

    Method AddIncompleteTest:Void(classInfo:ClassInfo, methodInfo:MethodInfo, message:String)
        result = INCOMPLETE
        incompleteMessages.Set(GetFQN(classInfo, methodInfo), message)
    End

    Method EndTest:Void(classInfo:ClassInfo, methodInfo:MethodInfo)
        PrintResult()
        currentClass = Null
        currentMethod = Null
    End

    Method EndTestSuite:Void(suite:TestSuite)
        PrintStats()

        failureMessages.Clear()
        incompleteMessages.Clear()
        skipMessages.Clear()
    End

    Private

    Method PrintResult:Void()
        If Not lastClass Or Not (lastClass.Name() = currentClass.Name())
            If lastClass Then Print ""
            Print "  " + currentClass.Name()
        End
        lastClass = currentClass

        Print "    " + currentMethod.Name() + " " + GetResultText()
    End

    Method GetResultText:String()
        Select result
        Case PASSED
            Return ""
        Case FAILED
            Return "[FAILED]"
        Case SKIPPED
            Return "[SKIPPED]"
        Case INCOMPLETE
            Return "[INCOMPLETE]"
        Default
            Throw New RuntimeException("Invalid result id: " + result)
        End
    End

    Method PrintStats:Void()
        Print ""

        PrintMessages(skipMessages, "skipped test")
        PrintMessages(incompleteMessages, "incomplete test")
        PrintMessages(failureMessages, "failure")

        PrintSummary()
        Print "Tests: "      + numTests                   +
            ", Failed: "     + failureMessages.Count()    +
            ", Skipped: "    + skipMessages.Count()       +
            ", Incomplete: " + incompleteMessages.Count() +
            "."
    End

    Method PrintMessages:Void(messages:StringMap<String>, desciption:String)
        If messages.Count() = 0 Then Return
        Print "There was " + messages.Count() + " " + desciption + ":~n"

        Local counter:Int = 0
        For Local key:String = EachIn messages.Keys()
            Local val:String = messages.Get(key).Replace("~n", "~n  ")
            counter += 1

            Print counter + ") " + key + "~n  " + val + "~n"
        End
    End

    Method PrintSummary:Void()
        If failureMessages.Count() > 0
            Print "FAILURES!"
        ElseIf skipMessages.Count() > 0 Or incompleteMessages.Count() > 0
            Print "OK, but incomplete or skipped tests!"
        Else
            Print "OK"
        End
    End

    Method GetFQN:String(classInfo:ClassInfo, methodInfo:MethodInfo)
        Local parts:String[] = classInfo.Name().Split(".")
        Local filename:String = "/".Join(parts[..-1]) + ".monkey"
        Local classname:String = parts[parts.Length() - 1]
        Return classname + "." + methodInfo.Name() + " in " + filename
    End
End

Strict

Private

Import bono.src.exceptions
Import reflection
Import testlistener
Import testsuite

Public

Class TestListenerSimple Implements TestListener
    Private

    Field lastClass:ClassInfo
    Field currentClass:ClassInfo
    Field currentMethod:MethodInfo
    Field result:Int = PASSED
    Field numPasses:Int
    Field failureMessages:StringMap<String> = New StringMap<String>()
    Field skipMessages:StringMap<String> = New StringMap<String>()
    Field incompleteMessages:StringMap<String> = New StringMap<String>()

    Const PASSED:Int = 0
    Const FAILED:Int = 1
    Const SKIPPED:Int = 2
    Const INCOMPLETE:Int = 3

    Public

    Method StartTestSuite:Void(suite:TestSuite)
    End

    Method StartTest:Void(classInfo:ClassInfo, methodInfo:MethodInfo)
        currentClass = classInfo
        currentMethod = methodInfo
        result = PASSED
    End

    Method AddFailure:Void(classInfo:ClassInfo, methodInfo:MethodInfo, message:String)
        result = FAILED
        failureMessages.Set(GetFQN(classInfo, methodInfo), message)
    End

    Method AddSkippedTest:Void(classInfo:ClassInfo, methodInfo:MethodInfo, message:String)
        result = SKIPPED
        skipMessages.Set(GetFQN(classInfo, methodInfo), message)
    End

    Method AddIncompleteTest:Void(classInfo:ClassInfo, methodInfo:MethodInfo, message:String)
        result = INCOMPLETE
        incompleteMessages.Set(GetFQN(classInfo, methodInfo), message)
    End

    Method EndTest:Void(classInfo:ClassInfo, methodInfo:MethodInfo)
        If result = PASSED Then numPasses += 1
        PrintResult()

        currentClass = Null
        currentMethod = Null
    End

    Method EndTestSuite:Void(suite:TestSuite)
        PrintStats()

        failureMessages.Clear()
        incompleteMessages.Clear()
        skipMessages.Clear()
    End

    Private

    Method PrintResult:Void()
        If Not lastClass Or Not (lastClass.Name() = currentClass.Name())
            If lastClass Then Print ""
            Print "  " + currentClass.Name()
        End
        lastClass = currentClass

        Print "    " + currentMethod.Name() + " " + GetResultText()
    End

    Method GetResultText:String()
        Select result
        Case PASSED
            Return ""
        Case FAILED
            Return "[FAILED]"
        Case SKIPPED
            Return "[SKIPPED]"
        Case INCOMPLETE
            Return "[INCOMPLETE]"
        Default
            Throw New RuntimeException("Invalid result id: " + result)
        End
    End

    Method PrintStats:Void()
        Print ""

        PrintMessages(skipMessages, "skipped test")
        PrintMessages(incompleteMessages, "incomplete test")
        PrintMessages(failureMessages, "failure")

        PrintSummary()
        Print "Tests: "      + numPasses                  +
            ", Failed: "     + failureMessages.Count()    +
            ", Skipped: "    + skipMessages.Count()       +
            ", Incomplete: " + incompleteMessages.Count() +
            "."
    End

    Method PrintMessages:Void(messages:StringMap<String>, desciption:String)
        If messages.Count() = 0 Then Return
        Print "There was " + messages.Count() + " " + desciption + ":~n"

        Local counter:Int = 0
        For Local key:String = EachIn messages.Keys()
            Local val:String = messages.Get(key).Replace("~n", "~n  ")
            counter += 1

            Print counter + ") " + key + "~n  " + val + "~n"
        End
    End

    Method PrintSummary:Void()
        If failureMessages.Count() > 0
            Print "FAILURES!"
        ElseIf skipMessages.Count() > 0 Or incompleteMessages.Count() > 0
            Print "OK, but incomplete or skipped tests!"
        Else
            Print "OK"
        End
    End

    Method GetFQN:String(classInfo:ClassInfo, methodInfo:MethodInfo)
        Local parts:String[] = classInfo.Name().Split(".")
        Local filename:String = "/".Join(parts[..-1]) + ".monkey"
        Local classname:String = parts[parts.Length() - 1]
        Return classname + "." + methodInfo.Name() + " in " + filename
    End
End


Changed Format to accept strings since Monkey will convert primitives automatically.

Strict

Import functions

' Monkey converts primitives to strings when calling this function.
Function Format:String(fmt:String, arg1:String="", arg2:String="", arg3:String="", arg4:String="", arg5:String="", arg6:String="", arg7:String="", arg8:String="", arg9:String="", arg10:String="")
	Return FormatImpl(fmt, StringObject(arg1), StringObject(arg2), StringObject(arg3), StringObject(arg4), StringObject(arg5), StringObject(arg6), StringObject(arg7), StringObject(arg8), StringObject(arg9), StringObject(arg10))
End

Private

' This is a very simple implementation of sprintf.  I can't guarantee that it'll be bulletproof at this time, so use at your own risk!
' Note that due to Monkey's lack of varargs, Format accepts a maximum of 10 optional arguments.  Arguments are parsed up until the first Null.
' TODO: round floats rather than truncating them
Function FormatImpl:String(fmt:String, arg1:Object=Null, arg2:Object=Null, arg3:Object=Null, arg4:Object=Null, arg5:Object=Null, arg6:Object=Null, arg7:Object=Null, arg8:Object=Null, arg9:Object=Null, arg10:Object=Null)
	Local args:Object[] = [arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10]
	Local argcount:Int = 0
	For Local i:Int = 0 Until args.Length
		If args[i] = Null Or StringObject(args[i]) = "" Then
			argcount = i
			Exit
		End
	Next
	Local rv:String = ""
	Local formatting:Bool = False
	Local escapingBackslash:Bool = False
	Local escapingPercent:Bool = False
	Local ptr:Int = 0
	Local argnum:Int = 0
	While ptr < fmt.Length()
		Local chr:String = fmt[ptr..ptr+1]
		ptr+=1
		' if we're escaping with a backslash, just add the character
		If escapingBackslash Then
			rv += chr
			escapingBackslash = False

		' if we're not escaping but we received a backslash, just enable escaping
		ElseIf chr = "\" Then
			escapingBackslash = True

		' if we're not formatting and receive a %, enable formatting and escape percent
		ElseIf Not formatting And chr = "%" Then
			formatting = True
			escapingPercent = True

		' if we're escaping the percent and receive it, disable formatting
		ElseIf escapingPercent And chr = "%" Then
			rv += chr
			escapingPercent = False
			formatting = False

		' if we're not formatting, just add the character
		ElseIf Not formatting Then
			rv += chr

		' we're formatting
		Else
			' check that we have enough args
			If argnum >= argcount Then Error("Error: Didn't receive enough arguments in call to Format")
			Local fmtarg:String = chr
			Local foundPeriod:Bool = False, foundMinus:Bool = False, foundPadding:Bool = False
			Local formatLengthStr:String = "", formatLength:Int = 0, formatDPStr:String = "", formatDP:Int = 0
			Local formatType:String = ""
			' extract the rest of the format tag
			If Not IsValidFormat(chr) Then
				While ptr < fmt.Length()
					fmtarg += fmt[ptr..ptr+1]
					ptr+=1
					If IsValidFormat(fmtarg[fmtarg.Length-1..]) Then Exit
				End
			End
			' set format type
			formatType = fmtarg[fmtarg.Length-1..]

			' get the last character as the format type and die if it's wrong
			If formatType = "" Then
				' error!
				Error "Error parsing format string!"
			End

			Local fmtargptr:Int = 0
			' check for minus
			If fmtarg[0..1] = "-" Then
				foundMinus = True
				fmtargptr+=1
			' check for padding
			ElseIf fmtarg[fmtargptr..fmtargptr+1] = "0" Then
				foundPadding = True
				fmtargptr+=1
			End
			' check for digits up to a period or a character
			While fmtargptr < fmtarg.Length()
				If IsValidFormat(fmtargptr) Then
					Exit
				ElseIf fmtarg[fmtargptr] >= 48 And fmtarg[fmtargptr] <= 57 Then
					If Not foundPeriod Then
						formatLengthStr += fmtarg[fmtargptr..fmtargptr+1]
					Else
						formatDPStr += fmtarg[fmtargptr..fmtargptr+1]
					End
				ElseIf fmtarg[fmtargptr..fmtargptr+1] = "." Then
					foundPeriod = True
				End
				fmtargptr+=1
			End

			formatting = False
			If formatLengthStr <> "" Then formatLength = Int(formatLengthStr)
			If formatDPStr <> "" Then formatDP = Int(formatDPStr)

			If formatType = "d" Then
				Local ds:String = ArgToString(args[argnum])
				While ds.Length() < formatLength
					If foundPadding Then
						ds = "0"+ds
					ElseIf foundMinus Then
						ds += " "
					Else
						ds = " "+ds
					End
				End
				rv += ds
			ElseIf formatType = "f" Then
				Local df:Float = ArgToFloat(args[argnum])
				' TODO: round float instead of truncating
				Local ds:String = df
				Local dp:Int = ds.Find(".")
				If dp < 0 Then dp = ds.Length()
				Local whole:String = ds[..dp]
				Local part:String = "0"
				If dp < ds.Length()-1 Then part = ds[dp+1..]
				If part.Length() > formatDP Then part = part[..formatDP]
				While part.Length() < formatDP
					part+="0"
				End
				ds = whole
				If part.Length()>0 Then ds+="."+part
				While ds.Length() < formatLength
					If foundPadding Then
						ds = "0"+ds
					ElseIf foundMinus Then
						ds += " "
					Else
						ds = " "+ds
					End
				End
				rv += ds
			ElseIf formatType = "c" Then
				If foundPadding Or foundMinus Then Error "Error parsing format string!"
				rv += String.FromChar(ArgToInt(args[argnum]))
			ElseIf formatType = "s" Or formatType = "S" Then
				If foundPadding Then Error "Error parsing format string!"
				Local ds:String = ArgToString(args[argnum])
				If formatType = "S" Then ds = ds.ToUpper()
				While ds.Length() < formatLength
					If foundMinus Then
						ds += " "
					Else
						ds = " "+ds
					End
				End
				rv += ds
			ElseIf formatType = "x" Or formatType = "X" Then
				Local ds:String = DecToHex(ArgToInt(args[argnum])).ToLower()
				If formatType = "X" Then ds = ds.ToUpper()
				While ds.Length() < formatLength
					If foundPadding Then
						ds = "0"+ds
					ElseIf foundMinus Then
						ds += " "
					Else
						ds = " "+ds
					End
				End
				rv += ds
			End
			argnum+=1
		End
	End
	Return rv
End

Function IsValidFormat:Bool(chr:String)
	Return chr = "d" Or chr = "f" Or chr = "s" Or chr = "S" Or chr = "c" Or chr = "x" Or chr = "X"
End

Function ArgToFloat:Float(arg:Object)
	If FloatObject(arg) Then Return FloatObject(arg)
	If IntObject(arg) Then Return IntObject(arg)
	If StringObject(arg) Then Return Float(StringObject(arg).value)
	Return 0
End

Function ArgToInt:Int(arg:Object)
	If FloatObject(arg) Then Return FloatObject(arg)
	If IntObject(arg) Then Return IntObject(arg)
	If StringObject(arg) Then Return Int(StringObject(arg).value)
	Return 0
End

Function ArgToString:String(arg:Object)
	If FloatObject(arg) Then Return String(FloatObject(arg).value)
	If IntObject(arg) Then Return String(IntObject(arg).value)
	If StringObject(arg) Then Return StringObject(arg)
	Return ""
End

Strict

Import functions

Function B:Object(v:Int)
	Return IntObject(v)
End

Function B:Object(v:Float)
	Return FloatObject(v)
End

Function B:Object(v:String)
	Return StringObject(v)
End

' This is a very simple implementation of sprintf.  I can't guarantee that it'll be bulletproof at this time, so use at your own risk!
' Monkey does not currently support autoboxing directly to Object, so unfortunately we need to manually do it.  Use the B() function for this.
' In other languages you might do the following:
'   sprintf(str, "%3d", 10); // C, C-like
'   str = String.format("%3d", 10); // Java
' But here you'll need to do this:
'   str = Format("%3d", B(10))
' Note that due to Monkey's lack of varargs, Format accepts a maximum of 10 optional arguments.  Arguments are parsed up until the first Null.
' TODO: round floats rather than truncating them
Function Format:String(fmt:String, arg1:Object=Null, arg2:Object=Null, arg3:Object=Null, arg4:Object=Null, arg5:Object=Null, arg6:Object=Null, arg7:Object=Null, arg8:Object=Null, arg9:Object=Null, arg10:Object=Null)
	Local args:Object[] = [arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10]
	Local argcount:Int = 0
	For Local i:Int = 0 Until args.Length
		If args[i] = Null Then
			argcount = i
			Exit
		End
	Next
	Local rv:String = ""
	Local formatting:Bool = False
	Local escapingBackslash:Bool = False
	Local escapingPercent:Bool = False
	Local ptr:Int = 0
	Local argnum:Int = 0
	While ptr < fmt.Length()
		Local chr:String = fmt[ptr..ptr+1]
		ptr+=1
		' if we're escaping with a backslash, just add the character
		If escapingBackslash Then
			rv += chr
			escapingBackslash = False

		' if we're not escaping but we received a backslash, just enable escaping
		ElseIf chr = "\" Then
			escapingBackslash = True

		' if we're not formatting and receive a %, enable formatting and escape percent
		ElseIf Not formatting And chr = "%" Then
			formatting = True
			escapingPercent = True

		' if we're escaping the percent and receive it, disable formatting
		ElseIf escapingPercent And chr = "%" Then
			rv += chr
			escapingPercent = False
			formatting = False

		' if we're not formatting, just add the character
		ElseIf Not formatting Then
			rv += chr

		' we're formatting
		Else
			' check that we have enough args
			If argnum >= argcount Then Error("Error: Didn't receive enough arguments in call to Format")
			Local fmtarg:String = chr
			Local foundPeriod:Bool = False, foundMinus:Bool = False, foundPadding:Bool = False
			Local formatLengthStr:String = "", formatLength:Int = 0, formatDPStr:String = "", formatDP:Int = 0
			Local formatType:String = ""
			' extract the rest of the format tag
			If Not IsValidFormat(chr) Then
				While ptr < fmt.Length()
					fmtarg += fmt[ptr..ptr+1]
					ptr+=1
					If IsValidFormat(fmtarg[fmtarg.Length-1..]) Then Exit
				End
			End
			' set format type
			formatType = fmtarg[fmtarg.Length-1..]

			' get the last character as the format type and die if it's wrong
			If formatType = "" Then
				' error!
				Error "Error parsing format string!"
			End

			Local fmtargptr:Int = 0
			' check for minus
			If fmtarg[0..1] = "-" Then
				foundMinus = True
				fmtargptr+=1
			' check for padding
			ElseIf fmtarg[fmtargptr..fmtargptr+1] = "0" Then
				foundPadding = True
				fmtargptr+=1
			End
			' check for digits up to a period or a character
			While fmtargptr < fmtarg.Length()
				If IsValidFormat(fmtargptr) Then
					Exit
				ElseIf fmtarg[fmtargptr] >= 48 And fmtarg[fmtargptr] <= 57 Then
					If Not foundPeriod Then
						formatLengthStr += fmtarg[fmtargptr..fmtargptr+1]
					Else
						formatDPStr += fmtarg[fmtargptr..fmtargptr+1]
					End
				ElseIf fmtarg[fmtargptr..fmtargptr+1] = "." Then
					foundPeriod = True
				End
				fmtargptr+=1
			End

			formatting = False
			If formatLengthStr <> "" Then formatLength = Int(formatLengthStr)
			If formatDPStr <> "" Then formatDP = Int(formatDPStr)

			If formatType = "d" Then
				Local ds:String = String(IntObject(args[argnum]))
				While ds.Length() < formatLength
					If foundPadding Then
						ds = "0"+ds
					ElseIf foundMinus Then
						ds += " "
					Else
						ds = " "+ds
					End
				End
				rv += ds
			ElseIf formatType = "f" Then
				Local df:Float = FloatObject(args[argnum])
				' TODO: round float instead of truncating
				Local ds:String = df
				Local dp:Int = ds.Find(".")
				If dp < 0 Then dp = ds.Length()
				Local whole:String = ds[..dp]
				Local part:String = "0"
				If dp < ds.Length()-1 Then part = ds[dp+1..]
				If part.Length() > formatDP Then part = part[..formatDP]
				While part.Length() < formatDP
					part+="0"
				End
				ds = whole
				If part.Length()>0 Then ds+="."+part
				While ds.Length() < formatLength
					If foundPadding Then
						ds = "0"+ds
					ElseIf foundMinus Then
						ds += " "
					Else
						ds = " "+ds
					End
				End
				rv += ds
			ElseIf formatType = "c" Then
				If foundPadding Or foundMinus Then Error "Error parsing format string!"
				rv += String.FromChar(IntObject(args[argnum]))
			ElseIf formatType = "s" Or formatType = "S" Then
				If foundPadding Then Error "Error parsing format string!"
				Local ds:String = StringObject(args[argnum])
				If formatType = "S" Then ds = ds.ToUpper()
				While ds.Length() < formatLength
					If foundMinus Then
						ds += " "
					Else
						ds = " "+ds
					End
				End
				rv += ds
			ElseIf formatType = "x" Or formatType = "X" Then
				Local ds:String = DecToHex(IntObject(args[argnum])).ToLower()
				If formatType = "X" Then ds = ds.ToUpper()
				While ds.Length() < formatLength
					If foundPadding Then
						ds = "0"+ds
					ElseIf foundMinus Then
						ds += " "
					Else
						ds = " "+ds
					End
				End
				rv += ds
			End
			argnum+=1
		End
	End
	Return rv
End

Private

Function IsValidFormat:Bool(chr:String)
	Return chr = "d" Or chr = "f" Or chr = "s" Or chr = "S" Or chr = "c" Or chr = "x" Or chr = "X"
End


update compiler opts for arm

update compiler opts for arm

#If LANG<>"cpp"
#Error "SIMD only supported on C++ targets"
#Endif

#CPP_MEM_ALIGNMENT = 16
#CPP_USE_ALIGNED_ALLOC = True
' Enable sse for gcc/mingw

#If TARGET="android" Or TARGET="ios"
' Use ARM's NEON
#CC_OPTS = "-mfpu=neon -mfloat-abi=softfp"
Import "native/simd.neon.cpp"
#Else
' Use SSE/AVX
#CC_OPTS = "-msse"
Import "native/simd.cpp"
#Endif

Extern

Class @Float32x4 Extends Object="SIMD_Float32x4"
End

Class SIMD
	' Float32x4 Factory
	Function MakeFloat32x4:Float32x4(x:Float, y:Float, z:Float, w:Float)
	Function MakeFloat32x4Zero:Float32x4()
	Function MakeFloat32x4Reverse:Float32x4(x:Float, y:Float, z:Float, w:Float)
	Function MakeFloat32x4One:Float32x4(x:Float)
	' Float32x4 Set Operations
	Function Set:Void(destination:Float32x4, x:Float, y:Float, z:Float, w:Float)
	Function SetZero:Void(destination:Float32x4)
	Function SetOne:Void(destination:Float32x4, x:Float)
	Function SetReverse:Void(destination:Float32x4, x:Float, y:Float, z:Float, w:Float)
	' Float32x4 Load and Store
	Function Load:Void(destination:Float32x4, source:Float[])
	Function Store:Void(destination:Float[], source:Float32x4)
	' Float32x4 Arithmetic
	Function Add:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function Sub:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function Mul:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function Min:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function Max:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function Sqrt:Void(destination:Float32x4, source:Float32x4)
	Function Rsqrt:Void(destination:Float32x4, source:Float32x4)
	' Float32x4 Logical Operations
	Function LogicAnd:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function LogicAndNot:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function LogicOr:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function LogicXor:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	' Float32x4 Compare Operations
	Function CompareEqual:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function CompareLowerThan:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function CompareLowerThanOrEqual:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function CompareGreaterThan:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function CompareGreaterThanOrEqual:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
End

' Float32x4 Shuffle.
Function SIMD_Shuffle:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4, mask:Int) = "SIMD_SHUFFLE"
Function SIMD_ShuffleHelper:Int(x:Int, y:Int, z:Int, w:Int) = "_MM_SHUFFLE"

#If LANG<>"cpp"
#Error "SIMD only supported on C++ targets"
#Endif

#CPP_MEM_ALIGNMENT = 16
#CPP_USE_ALIGNED_ALLOC = True
' Enable sse for gcc/mingw

#If TARGET="android" Or TARGET="ios"
' Use ARM's NEON
#CC_OPTS = "-mfpu=neon"
Import "native/simd.neon.cpp"
#Else
' Use SSE/AVX
#CC_OPTS = "-msse"
Import "native/simd.cpp"
#Endif

Extern

Class @Float32x4 Extends Object="SIMD_Float32x4"
End

Class SIMD
	' Float32x4 Factory
	Function MakeFloat32x4:Float32x4(x:Float, y:Float, z:Float, w:Float)
	Function MakeFloat32x4Zero:Float32x4()
	Function MakeFloat32x4Reverse:Float32x4(x:Float, y:Float, z:Float, w:Float)
	Function MakeFloat32x4One:Float32x4(x:Float)
	' Float32x4 Set Operations
	Function Set:Void(destination:Float32x4, x:Float, y:Float, z:Float, w:Float)
	Function SetZero:Void(destination:Float32x4)
	Function SetOne:Void(destination:Float32x4, x:Float)
	Function SetReverse:Void(destination:Float32x4, x:Float, y:Float, z:Float, w:Float)
	' Float32x4 Load and Store
	Function Load:Void(destination:Float32x4, source:Float[])
	Function Store:Void(destination:Float[], source:Float32x4)
	' Float32x4 Arithmetic
	Function Add:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function Sub:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function Mul:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function Min:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function Max:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function Sqrt:Void(destination:Float32x4, source:Float32x4)
	Function Rsqrt:Void(destination:Float32x4, source:Float32x4)
	' Float32x4 Logical Operations
	Function LogicAnd:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function LogicAndNot:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function LogicOr:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function LogicXor:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	' Float32x4 Compare Operations
	Function CompareEqual:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function CompareLowerThan:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function CompareLowerThanOrEqual:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function CompareGreaterThan:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
	Function CompareGreaterThanOrEqual:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4)
End

' Float32x4 Shuffle.
Function SIMD_Shuffle:Void(destination:Float32x4, operandR:Float32x4, operandL:Float32x4, mask:Int) = "SIMD_SHUFFLE"
Function SIMD_ShuffleHelper:Int(x:Int, y:Int, z:Int, w:Int) = "_MM_SHUFFLE"


remove src.payment from auto-import (bono.monkey)

Strict

Public

Import src.exceptions
Import src.fx
Import src.graphics
Import src.helper
Import src.io
Import src.kernel
Import src.scenes
Import src.tests
Import src.ui
Import src.utils

Strict

Public

Import src.exceptions
Import src.fx
Import src.graphics
Import src.helper
Import src.io
Import src.kernel
Import src.payment
Import src.scenes
Import src.tests
Import src.ui
Import src.utils


Added 'DebugException', etc.

Added 'DebugException', etc.

Strict

Public

' Preprocessor related:
#If CONFIG = "debug"
	#DEBUG_PRINT = True
#Else
	#DEBUG_PRINT = False
#End

#If CONFIG = "release"
	#DEBUG_PRINT_ON_ERROR = True
#Else
	#DEBUG_PRINT_ON_ERROR = False
#End

#DEBUG_PRINT_QUOTES = False

#DEBUG_THROW_ON_ERROR = True

' Friends:
#If UTIL_CONSOLE
	Friend console
#End

' Imports (Public):
' Nothing so far.

' Imports (Private):
Private

' Optional (Closed source):
#If UTIL_CONSOLE ' CONSOLE_IMPLEMENTED
	Import console
#End

Import monkey.lang

Public

' Global variable(s):
#If CONSOLE_IMPLEMENTED
	Global DebugConsole:Console = Null
#End

' Global variable(s) (Private):
Private

Const ErrorTemplate:String = "[ERROR] {Debug}: "
Const LogTemplate:String = "[INFO] {Debug}: "

Public

' Functions (Public):
#If CONSOLE_IMPLEMENTED
	Function DebugBind:Bool(C:Console)
		If (C = Null) Then Return False

		DebugConsole = C

		' Return the default response.
		Return True
	End
#End

Function DebugError:Void(E:Throwable, StopExecution:Bool=True)
	#If CONFIG = "debug"
		DebugStop()
	#Else
		DebugPrint("Unknown exception has been thrown; continuing anyway.")
	#End

	Return
End

Function DebugError:Void(Msg:String, StopExecution:Bool=True)
	#If DEBUG_PRINT
		' Local variable(s):
		Local TempMsg:String

		If (StopExecution) Then
			TempMsg = ErrorTemplate
		Else
			TempMsg = LogTemplate
		Endif

		' Generate the final message:
		Msg = TempMsg +

		#If DEBUG_PRINT_QUOTES
			Quote + Msg + Quote
		#Else
			Msg
		#End

		#If CONSOLE_IMPLEMENTED
			If (DebugConsole <> Null) Then
				DebugConsole.WriteLine(Msg, True)
			Else
		#End

		Print(Msg)

		#If CONSOLE_IMPLEMENTED
			Endif
		#End
	#Else
		' This may change later:
		If (StopExecution) Then
			Msg = ErrorTemplate + Msg
		Endif
	#End

	If (StopExecution) Then
		#If CONFIG = "debug"
			#If DEBUG_PRINT_ON_ERROR
				DebugStop()
			#Else
				EmitError(Msg)
			#End
		#Else
			#If DEBUG_PRINT
				Local FinalStr:String = (ErrorTemplate + Quote + "Attempt to stop execution failed. (Reason: Release-mode)" + Quote)

				#If CONSOLE_IMPLEMENTED
					If (DebugConsole <> Null) Then
						DebugConsole.WriteLine(FinalStr, True)
					Else
				#End

				Print(FinalStr)

				#If CONSOLE_IMPLEMENTED
					Endif
				#End
			#Else
				EmitError(Msg)
			#End
		#End
	Endif

	Return
End

Function DebugPrint:Void(Str:String="", StopExecution:Bool=False)
	If (Not StopExecution And Str.Length = 0) Then
		Print("")

		Return
	Endif

	DebugError(Str, StopExecution)

	Return
End

' Functions (Private):
Private

Function EmitError:Void(Message:String)
	#If DEBUG_THROW_ON_ERROR
		Throw New DebugException(Message)
	#Else
		Error(Message)
	#End

	Return
End

Public

' Classes:
Class DebugException Extends Throwable
	' Constructor(s):
	Method New(Message:String)
		Self.Message = Message
	End

	' Methods:
	Method ToString:String() ' Property
		Return Message
	End

	' Fields (Protected):
	Protected

	Field Message:String

	Public
End

Strict

Public

' Preprocessor related:
#If CONFIG = "debug"
	#DEBUG_PRINT = True
#Else
	#DEBUG_PRINT = False
#End

#If CONFIG = "release"
	#DEBUG_PRINT_ON_ERROR = True
#Else
	#DEBUG_PRINT_ON_ERROR = False
#End

#DEBUG_PRINT_QUOTES = False

' Friends:
#If UTIL_CONSOLE
	Friend console
#End

' Imports (Public):
' Nothing so far.

' Imports (Private):
Private

' Optional (Closed source):
#If UTIL_CONSOLE ' CONSOLE_IMPLEMENTED
	Import console
#End

Import monkey.lang

Public

' Global variable(s):
#If CONSOLE_IMPLEMENTED
	Global DebugConsole:Console = Null
#End

' Global variable(s) (Private):
Private

Const ErrorTemplate:String = "[ERROR] {Debug}: "
Const LogTemplate:String = "[INFO] {Debug}: "

Public

' Functions:
#If CONSOLE_IMPLEMENTED
	Function DebugBind:Bool(C:Console)
		If (C = Null) Then Return False

		DebugConsole = C

		' Return the default response.
		Return True
	End
#End

Function DebugError:Void(E:Throwable, StopExecution:Bool=True)
	#If CONFIG = "debug"
		DebugStop()
	#Else
		DebugPrint("Unknown exception has been thrown; continuing anyway.")
	#End

	Return
End

Function DebugError:Void(Msg:String, StopExecution:Bool=True)
	#If DEBUG_PRINT
		' Local variable(s):
		Local TempMsg:String

		If (StopExecution) Then
			TempMsg = ErrorTemplate
		Else
			TempMsg = LogTemplate
		Endif

		' Generate the final message:
		Msg = TempMsg +

		#If DEBUG_PRINT_QUOTES
			Quote + Msg + Quote
		#Else
			Msg
		#End

		#If CONSOLE_IMPLEMENTED
			If (DebugConsole <> Null) Then
				DebugConsole.WriteLine(Msg, True)
			Else
		#End

		Print(Msg)

		#If CONSOLE_IMPLEMENTED
			Endif
		#End
	#Else
		' This may change later:
		If (StopExecution) Then
			Msg = ErrorTemplate + Msg
		Endif
	#End

	If (StopExecution) Then
		#If CONFIG = "debug"
			#If DEBUG_PRINT_ON_ERROR
				DebugStop()
			#Else
				Error(Msg)
			#End
		#Else
			#If DEBUG_PRINT
				Local FinalStr:String = (ErrorTemplate + Quote + "Attempt to stop execution failed. (Reason: Release-mode)" + Quote)

				#If CONSOLE_IMPLEMENTED
					If (DebugConsole <> Null) Then
						DebugConsole.WriteLine(FinalStr, True)
					Else
				#End

				Print(FinalStr)

				#If CONSOLE_IMPLEMENTED
					Endif
				#End
			#Else
				Error(Msg)
			#End
		#End
	Endif

	Return
End

Function DebugPrint:Void(Str:String="", StopExecution:Bool=False)
	If (Not StopExecution And Str.Length = 0) Then
		Print("")

		Return
	Endif

	DebugError(Str, StopExecution)

	Return
End


Update Main.monkey

Update Main.monkey

updated to
Example Version 1.0

#rem
	Copyright (C) 2015  Stephan Duckerschein~n
	This Example shows how to use the Graphic Effects of Maniac.
	Just run this code and watch,
#end

import mojo
import maniac

Function Main:Int()
	New Example
End Function

Class Example Extends App

	Method OnCreate:Int()
		SetUpdateRate( 60 )

		#rem
			Initialises the Framework ; THIS IS IMPORT FOR EVERY MANIAC-Powerd Project
		#end
		initManiac()
	End Method

	Method OnUpdate()
		#rem
			This Method Updates some important stuff of the Maniac Frameworkd
		#end
		maniacUpdate()
	End Method

	Method OnRender:Int()
		Cls
		#rem
			Drawing a Rect.
			X,Y,Width,Height,Thickness all in Pxl
		#end
		SetColor 255,0,0
		Drw_Rect(DW*0.1,DH*0.1,DW*0.2,DH*0.2,5)

		#rem
			Drawing a Circle
		#end
		SetColor 255,255,255
		Drw_Ellipsis(DW*0.41,DH*0.2,DW*0.2,DH*0.2)

		#rem
			Drawing a 5x7 Grid
		#end
		SetColor 0,255,0
		Drw_Grid(DW*0.1,DH*0.35,DW*0.4,DH*0.4,5,7)

		#rem
			Draw some Lights
		#end
		Drw_Lightning(DW*0.6,DH*0.0,DW*0.63,DH*0.3,2)

		#rem
			Draw some Star.
		#end
		Drw_Stars(DW*0.55,DH*0.4,DW*0.4,DH*0.05,5,3)
	End Method
End Class


Scale cursor fixed

Scale cursor fixed

Strict

Import mojo

Import flxextern
Import flxbasic
Import flxstate
Import flxcamera
Import flxtimer
Import flxtext
Import flxg
Import flxu
Import flxtilemap
Import system.flxassetsmanager
Import system.flxfont
Import system.flxdebugger
Import system.flxreplay
Import plugin.timermanager

Import "data/flx_beep.mp3"

'#HTML5_SUSPEND_ON_BLUR_ENABLED = "true"

Class FlxGame extends App

	Field useSoundHotKeys:Bool

	Field useSystemCursor:Bool

	Field useVirtualResolution:Bool

	Field forceDebugger:Bool

	Field _state:FlxState

	Field _requestedState:FlxState

	Field _requestedReset:Bool

	Field _debugger:FlxDebugger

	Field _debuggerUp:Bool

	Field _replay:FlxReplay

	Field _replayRequested:Bool

	Field _recordingRequested:Bool

	Field _replaying:Bool

	Field _recording:Bool

	Field _replayCancelKeys:Int[]

	Field _replayTimer:Float

	Field _replayCallback:FlxReplayListener

Private
	Field _iState:FlxClass

	Field _created:Bool

	Field _total:Int

	Field _accumulator:Float

	Field _step:Float

	Field _maxAccumulation:Float

	Field _soundTrayTimer:Float

	Field _soundTrayWidth:Float

	Field _soundTrayHeight:Float

	Field _soundTrayX:Float

	Field _soundTrayY:Float

	Field _soundTrayVisible:Bool

	Field _soundTrayLabel:FlxText

	Field _updaterate:Int

	Field _framerate:Int

Public
	Method New(gameSizeX:Int, gameSizeY:Int, initialState:FlxClass, zoom:Float = 1, updaterate:Int = 60, framerate:Int = 30, useSystemCursor:Bool = False)
		_soundTrayTimer = 0
		_soundTrayWidth = 160
		_soundTrayHeight = 60
		_soundTrayX = 0
		_soundTrayY	= -_soundTrayHeight
		_soundTrayVisible = False

		FlxG.Init(Self, gameSizeX, gameSizeY, zoom)
		FlxG.Framerate = framerate
		FlxG.Updaterate = updaterate
		_total = 0

		_state = Null

		useSoundHotKeys = Not IsMobile()
		Self.useSystemCursor = useSystemCursor
		useVirtualResolution = True

		If (Not useSystemCursor) HideMouse()

		forceDebugger = False
		_debuggerUp = False

		_replay = New FlxReplay()
		_replayRequested = False
		_recordingRequested = False
		_replaying = False
		_recording = False

		_iState = initialState
		_requestedState = Null
		_requestedReset = True
		_created = False
	End Method

	Method OnCreate:Int()
		_InitData()
		_Step()

		_soundTrayX	= (FlxG.Width / 2) * FlxCamera.DefaultZoom * FlxG._DeviceScaleFactorX - (_soundTrayWidth / 2) + FlxG.Camera.X
		_soundTrayLabel = New FlxText(10, 32, _soundTrayWidth, "VOLUME")
		_soundTrayLabel.SetFormat(FlxText.SYSTEM_FONT, 16, FlxG.WHITE, FlxText.ALIGN_CENTER)
		Return 0
	End Method

	Method OnUpdate:Int()
		#If TARGET <> "ios" Or TARGET <> "android"
			If (useSoundHotKeys) Then
				If (KeyHit(KEY_0)) Then
					FlxG.Mute = Not FlxG.Mute

					If (FlxG.VolumeHandler <> Null) Then
						If (FlxG.Mute) Then
							FlxG.VolumeHandler.OnVolumeChange(0)
						Else
							FlxG.VolumeHandler.OnVolumeChange(FlxG.Volume())
						End If
					End If

					_ShowSoundTray()
				End If

				If (KeyHit(KEY_MINUS)) Then
					FlxG.Mute = False
					FlxG.Volume(FlxG.Volume() - .1)
					_ShowSoundTray()
				End If

				If (KeyHit(KEY_EQUALS)) Then
					FlxG.Mute = False
					FlxG.Volume(FlxG.Volume() + .1)
					_ShowSoundTray()
				End If
			End If

			_UpdateSoundTray(_step)
		#End

		Return 0
	End Method

	Method OnRender:Int()
		If (FlxG.Framerate <> _framerate Or FlxG.Updaterate <> _updaterate) Then
			_ResetFramerate()
		End If

		Local mark:Int = Millisecs()
		Local elapsedMS:Int = mark - _total
		_total = mark

		If (_debugger <> Null) Then
			'TODO!
		Else
			_accumulator += elapsedMS

			if (_accumulator > _maxAccumulation) Then
				_accumulator = _maxAccumulation
			End If

			While (_accumulator >= _step)
				_Step()
				_accumulator -= _step
			Wend
		End If

		FlxBasic._VisibleCount = 0

		_Draw()

		Return 0
	End Method

	Method OnSuspend:Int()
		ShowMouse()
		FlxG.PauseSounds()
		Return 0
	End Method

	Method OnResume:Int()
		If (Not _debuggerUp And Not useSystemCursor) Then
			HideMouse()
		End If

		FlxG.ResetInput()
		FlxG.ResumeSounds()
		Return 0
	End Method

	Method OnContentInit:Void()
	End Method

Private
	Method _ShowSoundTray:Void(silent:Bool = False)
		If (Not silent) Then
			FlxG.Play(FlxG.DATA_PREFIX + "beep")
		End If

		_soundTrayTimer = 1
		_soundTrayY = 0
		_soundTrayVisible = True
	End Method

	Method _SwitchState:Void()
		FlxG.ResetCameras()
		FlxG.ResetInput()
		FlxG.DestroySounds()
		FlxG.ClearBitmapCache()

		If (_debugger <> Null) Then
			'TODO!
		End If

		Local timeManager:TimerManager = FlxTimer.Manager()
		If (timeManager <> Null) timeManager.Clear()

		If (_state <> Null) _state.Destroy()

		_state = _requestedState
		_state.Create()

		If (FlxG.Framerate <> _framerate Or FlxG.Updaterate <> _updaterate) Then
			_ResetFramerate()
		End If
	End Method

	Method _Step:Void()
		If (_requestedReset) Then
			_requestedReset = False
			_requestedState = FlxState(_iState.CreateInstance())
			_replayTimer = 0
			_replayCancelKeys = []
			_Reset()
			FlxG.Reset()
		End If

		If (_recordingRequested) Then
			_recordingRequested = False
			_replay.Create(FlxG.GlobalSeed)
			_recording = True

			If (_debugger <> Null) Then
				'TODO
				FlxG.Log("FLIXEL: starting new flixel gameplay record.")
			End If

		ElseIf (_replayRequested)
			_replayRequested = False
			_replay.Rewind()
			FlxG.GlobalSeed = _replay.seed

			If (_debugger <> Null) Then
				'TODO
			End If

			_replaying = True
		End If

		If (_state <> _requestedState) _SwitchState()

		FlxBasic._ActiveCount = 0

		If (_replaying) Then
			If (_replayCancelKeys.Length() > 0 And Not KeyDown(192) And Not KeyDown(220)) Then
				Local i:Int = 0
				Local l:Int = _replayCancelKeys.Length()

				While (i < l)
					If (KeyDown(_replayCancelKeys[i])) Then
						If (_replayCallback <> Null) Then
							_replayCallback.OnReplayComplete()
							_replayCallback = Null
						Else
							FlxG.StopReplay()
						End If

						Exit
					End If

					i += 1
				Wend
			End If

			_replay.PlayNextFrame()

			If (_replayTimer > 0) Then
				_replayTimer -= _step

				If (_replayTimer <= 0) Then
					If (_replayCallback <> Null) Then
						_replayCallback.OnReplayComplete()
						_replayCallback = Null
					Else
						FlxG.StopReplay()
					End If
				End If
			End If

			If (_replaying And _replay.finished) Then
				FlxG.StopReplay()

				If (_replayCallback <> Null) Then
					_replayCallback.OnReplayComplete()
					_replayCallback = Null
				End If
			End If

			If (_debugger <> Null) Then
				'TODO
			End If
		Else
			FlxG.UpdateInput()
		End If

		If (_recording) Then
			_replay.RecordFrame()

			If (_debugger <> Null) Then
				'TODO
			End If
		End If

		_Update()

		If (_debugger <> Null) Then
			'TODO
		End If
	End Method

	Method _Update:Void()
		FlxG.Elapsed = FlxG.TimeScale * (_step / 1000.0)

		FlxG.UpdateSounds()
		FlxG.UpdatePlugins()
		_state.Update()
		FlxG.UpdateCameras()

		If (_debuggerUp) Then
			'TODO!
		End If
	End Method

	Method _UpdateSoundTray:Void(ms:Int)
		If (Not _soundTrayVisible) Return

		If (_soundTrayTimer > 0) Then
			_soundTrayTimer -= ms / 1000.0

		ElseIf (_soundTrayY > -_soundTrayHeight)
			_soundTrayY =_soundTrayY - (ms / 1000.0 * FlxG.Height * 2)

			If (_soundTrayY <= -_soundTrayHeight) Then
				_soundTrayVisible = False

				'TODO Save sound settings
			End If
		End If
	End Method

	Method _Draw:Void()
		Cls(FlxG._BgColor.r, FlxG._BgColor.g, FlxG._BgColor.b)
		Scale(FlxG._DeviceScaleFactorX, FlxG._DeviceScaleFactorY)

		FlxG._LastDrawingColor = FlxG.WHITE
		FlxG._LastDrawingBlend = GetBlend()
		FlxG._LastDrawingAlpha = GetAlpha()
		FlxG._CurrentCamera = Null

		Local i:Int = 0
		Local l:Int = FlxG.Cameras.Length()

		While(i < l)
			FlxG._CurrentCamera = FlxG.Cameras.Get(i)

			If (Not FlxG._CurrentCamera.active) Then
				i+=1
				Continue
			End If

			FlxG._CurrentCameraID = i

			If (FlxG._CurrentCamera = Null Or Not FlxG._CurrentCamera.exists Or Not FlxG._CurrentCamera.visible) Continue

			FlxG._CurrentCamera.DrawFX() 'not realy draw. Only calculation
			FlxG._CurrentCamera.Lock()
			_state.Draw()
			FlxG.DrawPlugins()
			FlxG._CurrentCamera.Unlock()

			i+=1
		Wend

		#If TARGET <> "ios" Or TARGET <> "android"
			_DrawSoundTray()
		#End

		FlxG.Mouse.Draw()
	End Method

	Method _DrawSoundTray:Void()
		If (_soundTrayVisible) Then
			Local globalVolume:Int = FlxU.Round(FlxG.Volume() * 10)
			If (FlxG.Mute) globalVolume = 0

			PushMatrix()
			Scale(1 / (FlxCamera.DefaultZoom * FlxG._DeviceScaleFactorX), 1 / (FlxCamera.DefaultZoom * FlxG._DeviceScaleFactorY))
			Translate(_soundTrayX, _soundTrayY)

			If (FlxG._LastDrawingAlpha <> .5) Then
				SetAlpha(.5)
				FlxG._LastDrawingAlpha = .5
			End If

			If (FlxG._LastDrawingColor <> FlxG.BLACK) Then
				SetColor(0, 0, 0)
			End If

			DrawRect(0, 0, _soundTrayWidth, _soundTrayHeight)

			SetColor(255, 255, 255)
			FlxG._LastDrawingColor = FlxG.WHITE

			Local bx:Int = 20
			Local by:Int = 28

			Local i:Int = 0
			While (i < 10)
				If (i < globalVolume) Then
					If (FlxG._LastDrawingAlpha <> 1) Then
						SetAlpha(1)
						FlxG._LastDrawingAlpha = 1
					End If

				Else
					If (FlxG._LastDrawingAlpha <> .5) Then
						SetAlpha(.5)
						FlxG._LastDrawingAlpha = .5
					End If
				End If

				DrawRect(bx, by, 8, i * 2)

				bx += 12
				by -= 2
				i += 1
			Wend

			_soundTrayLabel.Draw()

			PopMatrix()
		End If
	End Method

	Method _Reset:Void()
		If (FlxG.Framerate <> _framerate Or FlxG.Updaterate <> _updaterate) Then
			_ResetFramerate()
		End If

		Seed = SystemMillisecs()

		FlxG.DeviceWidth = DeviceWidth()
		FlxG.DeviceHeight = DeviceHeight()

		If (useVirtualResolution) Then
			FlxG._DeviceScaleFactorX = FlxG.DeviceWidth / Float(FlxG.Width)
			FlxG._DeviceScaleFactorY = FlxG.DeviceHeight / Float(FlxG.Height)
		Else
			FlxG._DeviceScaleFactorX = 1
			FlxG._DeviceScaleFactorY = 1
		End If
	End Method

	Method _ResetFramerate:Void()
		SetUpdateRate(FlxG.Framerate)

		_step = 1000.0 / FlxG.Updaterate
		_maxAccumulation = 2000.0 / FlxG.Framerate - 1

		If (_maxAccumulation < _step) Then
			_maxAccumulation = _step
		End If

		_updaterate = FlxG.Updaterate
		_framerate = FlxG.Framerate
	End Method

	Method _InitData:Void()
		FlxAssetsManager.Init()

		Local minSystemFontSize:Int = 8
		Local maxSystemFontSize:Int = 24
		Local fontPathPrefix:String = FlxG.DATA_PREFIX + FlxText.SYSTEM_FONT + "_font_"

		Local system:FlxFont = 	FlxAssetsManager.AddFont(FlxText.SYSTEM_FONT)

		For Local size:Int = minSystemFontSize To maxSystemFontSize
			system.SetPath(size, fontPathPrefix + Min(size, 17) + ".png")
		Next

		FlxAssetsManager.AddImage(FlxG.DATA_PREFIX + "default", FlxG.DATA_PREFIX + "default.png")
		FlxAssetsManager.AddImage(FlxG.DATA_PREFIX + "button", FlxG.DATA_PREFIX + "button.png")
		FlxAssetsManager.AddImage(FlxTilemap.AUTOTILES, FlxTilemap.AUTOTILES + ".png")
		FlxAssetsManager.AddImage(FlxTilemap.AUTOTILES_ALT, FlxTilemap.AUTOTILES_ALT + ".png")
		FlxAssetsManager.AddCursor(FlxG.DATA_PREFIX + "cursor", FlxG.DATA_PREFIX + "cursor.png")
		FlxAssetsManager.AddSound(FlxG.DATA_PREFIX + "beep", FlxG.DATA_PREFIX + "beep.mp3")

		Self.OnContentInit()
	End Method
End Class

Private
Class FlxFPSCounter

	Field FPS:Int

Private
	Field _FPSCounter:Int

	Field _lastUpdateTime:Int

Public
	Method New()
		Reset()
	End Method

	Method Update:Void()
		If (_lastUpdateTime = 0) Then
			_lastUpdateTime = Millisecs()
		ElseIf (Millisecs() - _lastUpdateTime >= 1000) Then
			FPS = _FPSCounter
			_lastUpdateTime = Millisecs()
			_FPSCounter = 0
		End If

		_FPSCounter += 1
	End Method

	Method Reset:Void()
		FPS = FlxG.Framerate
		_FPSCounter = 0
		_lastUpdateTime = 0
	End Method

End Class

Strict

Import mojo

Import flxextern
Import flxbasic
Import flxstate
Import flxcamera
Import flxtimer
Import flxtext
Import flxg
Import flxu
Import flxtilemap
Import system.flxassetsmanager
Import system.flxfont
Import system.flxdebugger
Import system.flxreplay
Import plugin.timermanager

Import "data/flx_beep.mp3"

'#HTML5_SUSPEND_ON_BLUR_ENABLED = "true"

Class FlxGame extends App

	Field useSoundHotKeys:Bool

	Field useSystemCursor:Bool

	Field useVirtualResolution:Bool

	Field forceDebugger:Bool

	Field _state:FlxState

	Field _requestedState:FlxState

	Field _requestedReset:Bool

	Field _debugger:FlxDebugger

	Field _debuggerUp:Bool

	Field _replay:FlxReplay

	Field _replayRequested:Bool

	Field _recordingRequested:Bool

	Field _replaying:Bool

	Field _recording:Bool

	Field _replayCancelKeys:Int[]

	Field _replayTimer:Float

	Field _replayCallback:FlxReplayListener

Private
	Field _iState:FlxClass

	Field _created:Bool

	Field _total:Int

	Field _accumulator:Float

	Field _step:Float

	Field _maxAccumulation:Float

	Field _soundTrayTimer:Float

	Field _soundTrayWidth:Float

	Field _soundTrayHeight:Float

	Field _soundTrayX:Float

	Field _soundTrayY:Float

	Field _soundTrayVisible:Bool

	Field _soundTrayLabel:FlxText

	Field _updaterate:Int

	Field _framerate:Int

Public
	Method New(gameSizeX:Int, gameSizeY:Int, initialState:FlxClass, zoom:Float = 1, updaterate:Int = 60, framerate:Int = 30, useSystemCursor:Bool = False)
		_soundTrayTimer = 0
		_soundTrayWidth = 160
		_soundTrayHeight = 60
		_soundTrayX = 0
		_soundTrayY	= -_soundTrayHeight
		_soundTrayVisible = False

		FlxG.Init(Self, gameSizeX, gameSizeY, zoom)
		FlxG.Framerate = framerate
		FlxG.Updaterate = updaterate
		_total = 0

		_state = Null

		useSoundHotKeys = Not IsMobile()
		Self.useSystemCursor = useSystemCursor
		useVirtualResolution = True

		If (Not useSystemCursor) HideMouse()

		forceDebugger = False
		_debuggerUp = False

		_replay = New FlxReplay()
		_replayRequested = False
		_recordingRequested = False
		_replaying = False
		_recording = False

		_iState = initialState
		_requestedState = Null
		_requestedReset = True
		_created = False
	End Method

	Method OnCreate:Int()
		_InitData()
		_Step()

		_soundTrayX	= (FlxG.Width / 2) * FlxCamera.DefaultZoom * FlxG._DeviceScaleFactorX - (_soundTrayWidth / 2) + FlxG.Camera.X
		_soundTrayLabel = New FlxText(10, 32, _soundTrayWidth, "VOLUME")
		_soundTrayLabel.SetFormat(FlxText.SYSTEM_FONT, 16, FlxG.WHITE, FlxText.ALIGN_CENTER)
		Return 0
	End Method

	Method OnUpdate:Int()
		#If TARGET <> "ios" Or TARGET <> "android"
			If (useSoundHotKeys) Then
				If (KeyHit(KEY_0)) Then
					FlxG.Mute = Not FlxG.Mute

					If (FlxG.VolumeHandler <> Null) Then
						If (FlxG.Mute) Then
							FlxG.VolumeHandler.OnVolumeChange(0)
						Else
							FlxG.VolumeHandler.OnVolumeChange(FlxG.Volume())
						End If
					End If

					_ShowSoundTray()
				End If

				If (KeyHit(KEY_MINUS)) Then
					FlxG.Mute = False
					FlxG.Volume(FlxG.Volume() - .1)
					_ShowSoundTray()
				End If

				If (KeyHit(KEY_EQUALS)) Then
					FlxG.Mute = False
					FlxG.Volume(FlxG.Volume() + .1)
					_ShowSoundTray()
				End If
			End If

			_UpdateSoundTray(_step)
		#End

		Return 0
	End Method

	Method OnRender:Int()
		If (FlxG.Framerate <> _framerate Or FlxG.Updaterate <> _updaterate) Then
			_ResetFramerate()
		End If

		Local mark:Int = Millisecs()
		Local elapsedMS:Int = mark - _total
		_total = mark

		If (_debugger <> Null) Then
			'TODO!
		Else
			_accumulator += elapsedMS

			if (_accumulator > _maxAccumulation) Then
				_accumulator = _maxAccumulation
			End If

			While (_accumulator >= _step)
				_Step()
				_accumulator -= _step
			Wend
		End If

		FlxBasic._VisibleCount = 0

		_Draw()

		Return 0
	End Method

	Method OnSuspend:Int()
		ShowMouse()
		FlxG.PauseSounds()
		Return 0
	End Method

	Method OnResume:Int()
		If (Not _debuggerUp And Not useSystemCursor) Then
			HideMouse()
		End If

		FlxG.ResetInput()
		FlxG.ResumeSounds()
		Return 0
	End Method

	Method OnContentInit:Void()
	End Method

Private
	Method _ShowSoundTray:Void(silent:Bool = False)
		If (Not silent) Then
			FlxG.Play(FlxG.DATA_PREFIX + "beep")
		End If

		_soundTrayTimer = 1
		_soundTrayY = 0
		_soundTrayVisible = True
	End Method

	Method _SwitchState:Void()
		FlxG.ResetCameras()
		FlxG.ResetInput()
		FlxG.DestroySounds()
		FlxG.ClearBitmapCache()

		If (_debugger <> Null) Then
			'TODO!
		End If

		Local timeManager:TimerManager = FlxTimer.Manager()
		If (timeManager <> Null) timeManager.Clear()

		If (_state <> Null) _state.Destroy()

		_state = _requestedState
		_state.Create()

		If (FlxG.Framerate <> _framerate Or FlxG.Updaterate <> _updaterate) Then
			_ResetFramerate()
		End If
	End Method

	Method _Step:Void()
		If (_requestedReset) Then
			_requestedReset = False
			_requestedState = FlxState(_iState.CreateInstance())
			_replayTimer = 0
			_replayCancelKeys = []
			_Reset()
			FlxG.Reset()
		End If

		If (_recordingRequested) Then
			_recordingRequested = False
			_replay.Create(FlxG.GlobalSeed)
			_recording = True

			If (_debugger <> Null) Then
				'TODO
				FlxG.Log("FLIXEL: starting new flixel gameplay record.")
			End If

		ElseIf (_replayRequested)
			_replayRequested = False
			_replay.Rewind()
			FlxG.GlobalSeed = _replay.seed

			If (_debugger <> Null) Then
				'TODO
			End If

			_replaying = True
		End If

		If (_state <> _requestedState) _SwitchState()

		FlxBasic._ActiveCount = 0

		If (_replaying) Then
			If (_replayCancelKeys.Length() > 0 And Not KeyDown(192) And Not KeyDown(220)) Then
				Local i:Int = 0
				Local l:Int = _replayCancelKeys.Length()

				While (i < l)
					If (KeyDown(_replayCancelKeys[i])) Then
						If (_replayCallback <> Null) Then
							_replayCallback.OnReplayComplete()
							_replayCallback = Null
						Else
							FlxG.StopReplay()
						End If

						Exit
					End If

					i += 1
				Wend
			End If

			_replay.PlayNextFrame()

			If (_replayTimer > 0) Then
				_replayTimer -= _step

				If (_replayTimer <= 0) Then
					If (_replayCallback <> Null) Then
						_replayCallback.OnReplayComplete()
						_replayCallback = Null
					Else
						FlxG.StopReplay()
					End If
				End If
			End If

			If (_replaying And _replay.finished) Then
				FlxG.StopReplay()

				If (_replayCallback <> Null) Then
					_replayCallback.OnReplayComplete()
					_replayCallback = Null
				End If
			End If

			If (_debugger <> Null) Then
				'TODO
			End If
		Else
			FlxG.UpdateInput()
		End If

		If (_recording) Then
			_replay.RecordFrame()

			If (_debugger <> Null) Then
				'TODO
			End If
		End If

		_Update()

		If (_debugger <> Null) Then
			'TODO
		End If
	End Method

	Method _Update:Void()
		FlxG.Elapsed = FlxG.TimeScale * (_step / 1000.0)

		FlxG.UpdateSounds()
		FlxG.UpdatePlugins()
		_state.Update()
		FlxG.UpdateCameras()

		If (_debuggerUp) Then
			'TODO!
		End If
	End Method

	Method _UpdateSoundTray:Void(ms:Int)
		If (Not _soundTrayVisible) Return

		If (_soundTrayTimer > 0) Then
			_soundTrayTimer -= ms / 1000.0

		ElseIf (_soundTrayY > -_soundTrayHeight)
			_soundTrayY =_soundTrayY - (ms / 1000.0 * FlxG.Height * 2)

			If (_soundTrayY <= -_soundTrayHeight) Then
				_soundTrayVisible = False

				'TODO Save sound settings
			End If
		End If
	End Method

	Method _Draw:Void()
		Cls(FlxG._BgColor.r, FlxG._BgColor.g, FlxG._BgColor.b)
		Scale(FlxG._DeviceScaleFactorX, FlxG._DeviceScaleFactorY)

		FlxG._LastDrawingColor = FlxG.WHITE
		FlxG._LastDrawingBlend = GetBlend()
		FlxG._LastDrawingAlpha = GetAlpha()
		FlxG._CurrentCamera = Null

		Local i:Int = 0
		Local l:Int = FlxG.Cameras.Length()

		While(i < l)
			FlxG._CurrentCamera = FlxG.Cameras.Get(i)

			If (Not FlxG._CurrentCamera.active) Then
				i+=1
				Continue
			End If

			FlxG._CurrentCameraID = i

			If (FlxG._CurrentCamera = Null Or Not FlxG._CurrentCamera.exists Or Not FlxG._CurrentCamera.visible) Continue

			FlxG._CurrentCamera.DrawFX() 'not realy draw. Only calculation
			FlxG._CurrentCamera.Lock()
			_state.Draw()
			FlxG.DrawPlugins()
			FlxG._CurrentCamera.Unlock()

			i+=1
		Wend

		#If TARGET <> "ios" Or TARGET <> "android"
			_DrawSoundTray()
		#End

		FlxG.Mouse.Draw()
	End Method

	Method _DrawSoundTray:Void()
		If (_soundTrayVisible) Then
			Local globalVolume:Int = FlxU.Round(FlxG.Volume() * 10)
			If (FlxG.Mute) globalVolume = 0

			Scale(1 / (FlxCamera.DefaultZoom * FlxG._DeviceScaleFactorX), 1 / (FlxCamera.DefaultZoom * FlxG._DeviceScaleFactorY))

			PushMatrix()
			Translate(_soundTrayX, _soundTrayY)

			If (FlxG._LastDrawingAlpha <> .5) Then
				SetAlpha(.5)
				FlxG._LastDrawingAlpha = .5
			End If

			If (FlxG._LastDrawingColor <> FlxG.BLACK) Then
				SetColor(0, 0, 0)
			End If

			DrawRect(0, 0, _soundTrayWidth, _soundTrayHeight)

			SetColor(255, 255, 255)
			FlxG._LastDrawingColor = FlxG.WHITE

			Local bx:Int = 20
			Local by:Int = 28

			Local i:Int = 0
			While (i < 10)
				If (i < globalVolume) Then
					If (FlxG._LastDrawingAlpha <> 1) Then
						SetAlpha(1)
						FlxG._LastDrawingAlpha = 1
					End If

				Else
					If (FlxG._LastDrawingAlpha <> .5) Then
						SetAlpha(.5)
						FlxG._LastDrawingAlpha = .5
					End If
				End If

				DrawRect(bx, by, 8, i * 2)

				bx += 12
				by -= 2
				i += 1
			Wend

			_soundTrayLabel.Draw()

			PopMatrix()
		End If
	End Method

	Method _Reset:Void()
		If (FlxG.Framerate <> _framerate Or FlxG.Updaterate <> _updaterate) Then
			_ResetFramerate()
		End If

		Seed = SystemMillisecs()

		FlxG.DeviceWidth = DeviceWidth()
		FlxG.DeviceHeight = DeviceHeight()

		If (useVirtualResolution) Then
			FlxG._DeviceScaleFactorX = FlxG.DeviceWidth / Float(FlxG.Width)
			FlxG._DeviceScaleFactorY = FlxG.DeviceHeight / Float(FlxG.Height)
		Else
			FlxG._DeviceScaleFactorX = 1
			FlxG._DeviceScaleFactorY = 1
		End If
	End Method

	Method _ResetFramerate:Void()
		SetUpdateRate(FlxG.Framerate)

		_step = 1000.0 / FlxG.Updaterate
		_maxAccumulation = 2000.0 / FlxG.Framerate - 1

		If (_maxAccumulation < _step) Then
			_maxAccumulation = _step
		End If

		_updaterate = FlxG.Updaterate
		_framerate = FlxG.Framerate
	End Method

	Method _InitData:Void()
		FlxAssetsManager.Init()

		Local minSystemFontSize:Int = 8
		Local maxSystemFontSize:Int = 24
		Local fontPathPrefix:String = FlxG.DATA_PREFIX + FlxText.SYSTEM_FONT + "_font_"

		Local system:FlxFont = 	FlxAssetsManager.AddFont(FlxText.SYSTEM_FONT)

		For Local size:Int = minSystemFontSize To maxSystemFontSize
			system.SetPath(size, fontPathPrefix + Min(size, 17) + ".png")
		Next

		FlxAssetsManager.AddImage(FlxG.DATA_PREFIX + "default", FlxG.DATA_PREFIX + "default.png")
		FlxAssetsManager.AddImage(FlxG.DATA_PREFIX + "button", FlxG.DATA_PREFIX + "button.png")
		FlxAssetsManager.AddImage(FlxTilemap.AUTOTILES, FlxTilemap.AUTOTILES + ".png")
		FlxAssetsManager.AddImage(FlxTilemap.AUTOTILES_ALT, FlxTilemap.AUTOTILES_ALT + ".png")
		FlxAssetsManager.AddCursor(FlxG.DATA_PREFIX + "cursor", FlxG.DATA_PREFIX + "cursor.png")
		FlxAssetsManager.AddSound(FlxG.DATA_PREFIX + "beep", FlxG.DATA_PREFIX + "beep.mp3")

		Self.OnContentInit()
	End Method
End Class

Private
Class FlxFPSCounter

	Field FPS:Int

Private
	Field _FPSCounter:Int

	Field _lastUpdateTime:Int

Public
	Method New()
		Reset()
	End Method

	Method Update:Void()
		If (_lastUpdateTime = 0) Then
			_lastUpdateTime = Millisecs()
		ElseIf (Millisecs() - _lastUpdateTime >= 1000) Then
			FPS = _FPSCounter
			_lastUpdateTime = Millisecs()
			_FPSCounter = 0
		End If

		_FPSCounter += 1
	End Method

	Method Reset:Void()
		FPS = FlxG.Framerate
		_FPSCounter = 0
		_lastUpdateTime = 0
	End Method

End Class