Changed base64 implementation to use an int array instead of appending strings.  Improved performance on Android for large data sets from minutes to milliseconds!

#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"
	#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"
	#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 > src2.Length Then Exit ' This shouldn't happen with base64, so something's wrong!
		b = BASE64_ARRAY[src2[i+1]]
		If i+2 < src2.Length Then c = BASE64_ARRAY[src2[i+2]] Else c = 64
		If i+3 < src2.Length 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"
	#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"
	#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

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)
	Local a:Int, b:Int, c:Int, d:Int, i:Int, j:Int
	Local src2:String = ""
	' 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 []

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

	i = 0
	j = 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
		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 >= src2.Length
	Return rv
End

' constants

Const BASE64_CHARS:String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="

' 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 AndroidAndy's horizontal menu enhancement for SimpleGUI. To use:

menu = New SimpleMenu("ButtonOver", "ButtonClick", 0, 0, 10, True, HORIZONTAL)

Final parameter is the orientation of the menu (HORIZONTAL & VERTICAL)

Import diddy

Const VERTICAL:Int = 0
Const HORIZONTAL:Int = 1

Class SimpleMenu Extends List<SimpleButton>
	Field x:Float, y:Float
	Field buttonGap:Int = 0
	Field mouseOverName:String = ""
	Field clickedName:String = ""
	Field clearClickedName:Int = 1
	Field nextX:Int = 0
	Field nextY:Int = 0
	Field w:Int, h:Int
	Field addGap:Int = 0
	Field soundMouseOver:GameSound
	Field soundClick:GameSound
	Field useVirtualRes:Bool = False
	Field orientation:Int = VERTICAL

	Method New()
		Error "Please use a different constructor"
	End

	Method New(soundMouseOverFile$, soundClickFile$, x:Int, y:Int, gap:Int, useVirtualRes:Bool, orientation:Int = VERTICAL)
		Init(soundMouseOverFile, soundClickFile, x, y, gap, useVirtualRes, orientation)
	End

	Method Init:Void(soundMouseOverFile:String="", soundClickFile:String="", x:Float, y:Float, gap:Int, useVirtualRes:Bool, orientation:Int)
		Self.Clear()
		Self.useVirtualRes = useVirtualRes
		Self.orientation = orientation
		Self.x = x
		Self.y = y
		nextX = x
		nextY = y
		Self.buttonGap = gap
		w = 0
		h = 0
		mouseOverName = ""
		clickedName = ""
		addGap = 0
		If soundMouseOverFile<>"" Then
			soundMouseOver = New GameSound
			soundMouseOver.Load(soundMouseOverFile)
		End
		If soundClickFile<>"" Then
			soundClick = New GameSound
			soundClick.Load(soundClickFile)
		End
	End

	Method CalcWidth:Int()
		Local left:Int=10000
		Local right:Int=-10000
		Local b:SimpleButton
		For b = EachIn Self
			If b.x < left Then left = b.x
			If b.x+b.image.w > right Then right = b.x + b.image.w
		Next
		w = right - left
		Return w
	End

	Method CalcHeight:Int()
		Local top:Int=10000
		Local bot:Int=-10000
		Local b:SimpleButton
		For b = EachIn Self
			If b.y < top Then top = b.y
			If b.y+b.image.h > bot Then bot = b.y + b.image.h
		Next
		h = bot - top
		Return h
	End

	Method SetX:Void(thex#)
		CalcLeft()
		Local oldx# = x
		x = thex
		Local diff# = x - oldx
		Local b:SimpleButton
		For b = EachIn Self
			b.MoveBy(diff,0)
		Next
	End

	Method SetY:Void(they#)
		CalcTop()
		Local oldy# = y
		y = they
		Local diff# = y-oldy
		Local b:SimpleButton
		For b = EachIn Self
			b.MoveBy(0,diff)
		Next
	End

	Method CalcLeft:Void()
		x = 10000
		Local b:SimpleButton
		For b = EachIn Self
			If b.x <x Then x = b.x
		Next
	End

	Method CalcTop:Void()
		y = 10000
		Local b:SimpleButton
		For b = EachIn Self
			If b.y < y Then y = b.y
		Next
	End

	Method CentreHoriz:Void()
		CalcWidth()
		if useVirtualRes
			SetX((SCREEN_WIDTH-w)/2)
		Else
			SetX((DEVICE_WIDTH-w)/2)
		End
	End

	Method CentreVert:Void()
		CalcHeight()
		if useVirtualRes
			SetY((SCREEN_HEIGHT-h)/2)
		Else
			SetY((DEVICE_HEIGHT-h)/2)
		End
	End

	Method Centre:Void()
		CentreHoriz()
		CentreVert()
	End

	Method SetMenuAlpha:Void(alpha:Float)
		Local b:SimpleButton
		For b = EachIn Self
			b.alpha = alpha
		Next
	End

	Method AddButton:SimpleButton(buttonImageFile:String, mouseOverFile:String, name:String="")
		Local b:SimpleButton = ProcessAddButton(buttonImageFile, mouseOverFile, name)
		If orientation = VERTICAL
			IncreaseHeight(b)
		Else
			IncreaseWidth(b)
		End
		Return b
	End

	Method IncreaseHeight:Void(b:SimpleButton)
		nextY = nextY + b.image.h + buttonGap

		h = h + b.image.h
		If addGap Then
			h = h + buttonGap
		Else
			addGap = 1
		End
	End

	Method IncreaseWidth:Void(b:SimpleButton)
		nextX = nextX + b.image.w + buttonGap

		w = w + b.image.w
		If addGap Then
			w = w + buttonGap
		Else
			addGap = 1
		End
	End

	Method ProcessAddButton:SimpleButton(buttonImageFile:String, mouseOverFile:String, name:String)
		Local b:SimpleButton = New SimpleButton
		b.Load(buttonImageFile, mouseOverFile)
		b.useVirtualRes = Self.useVirtualRes
		b.orientation = Self.orientation
		If name <> "" Then b.name = name.ToUpper()
		If orientation = VERTICAL
			b.CentreX(nextY)
		Else
			b.MoveTo(nextX, nextY)
		End

		b.soundMouseOver = soundMouseOver
		b.soundClick = soundClick
		AddLast(b)
		Return b
	End

	Method FindButton:SimpleButton(name:String)
		name = name.ToUpper()
		Local b:SimpleButton
		For b = EachIn Self
			If b.name = name Then Return b
		Next
		Return Null
	End

	Method Clicked:Int(name:String)
		name = name.ToUpper()
		If name = clickedName
			If clearClickedName Then clickedName = ""
			Return 1
		Else
			Return 0
		End
	End

	Method Update:Int()
		If game.screenFade.active
			Return 0
		EndIf
		clickedName = ""
		Local b:SimpleButton
		For b = EachIn Self
			b.Update()
			If b.mouseOver Then mouseOverName = b.name
			If b.clicked Then clickedName = b.name
		Next
		Return 1
	End

	Method Precache:Void()
		For Local b:SimpleButton = EachIn Self
			b.Precache()
		Next
	End

	Method Draw:Void()
		For Local b:SimpleButton = EachIn Self
			b.Draw()
		Next
	End
End

Class SimpleButton Extends Sprite
	Field active:Int = 1
	Field clicked:Int = 0
	Field mouseOver:Int = 0
	Field disabled:Bool = False
	Field soundMouseOver:GameSound
	Field soundClick:GameSound
	Field imageMouseOver:GameImage
	Field useVirtualRes:Bool = False
	Field orientation:Int = VERTICAL

	Method Precache:Void()
		If image<>null
			Super.Precache()
		End
	End

	Method Draw:Void()
		If active = 0 Then Return
		SetAlpha Self.alpha
		if mouseOver
			DrawImage Self.imageMouseOver.image, x, y
		else
			DrawImage Self.image.image, x, y
		EndIf
		SetAlpha 1
	End

	Method Click:Void()
		If clicked = 0
			clicked = 1
			If soundClick <> null
				soundClick.Play()
			End
		End
	End

	Method CentreX:Void(yCoord:Int)
		if useVirtualRes
			MoveTo((SCREEN_WIDTH-image.w)/2, yCoord)
		Else
			MoveTo((DEVICE_WIDTH-image.w)/2, yCoord)
		End

	End

	Method CentreY:Void(xCoord:Int)
		If useVirtualRes
			MoveTo((SCREEN_HEIGHT-image.h)/2, xCoord)
		Else
			MoveTo((DEVICE_HEIGHT-image.h)/2, xCoord)
		End
	End

	Method MoveBy:Void(dx:Float,dy:Float)
		x+=dx
		y+=dy
	End Method

	Method MoveTo:Void(dx:Float,dy:Float)
		x=dx
		y=dy
	End Method

	Method Load:Void(buttonImage:String, mouseOverImage:String = "", soundMouseOverFile:String="", soundClickFile:String="")
		Self.image = New GameImage
		image.Load(game.images.path + buttonImage, False)

		if  mouseOverImage <> ""
			imageMouseOver = New GameImage
			imageMouseOver.Load(game.images.path + mouseOverImage, False)
		End

		name = StripAll(buttonImage.ToUpper())

		If soundMouseOverFile<>"" Then
			soundMouseOver = New GameSound
			soundMouseOver.Load(soundMouseOverFile)
		End
		If soundClickFile<>"" Then
			soundClick = New GameSound
			soundClick.Load(soundClickFile)
		End
	End

	Method Update:Void()
		If active = 0 or disabled Then Return
		Local mx:Int = game.mouseX
		Local my:Int = game.mouseY
		if not useVirtualRes
			mx = MouseX()
			my = MouseY()
		End
		If mx >= x And mx < x+image.w And my >= y And my < y+image.h Then
			If mouseOver = 0
				if soundMouseOver <> null
					soundMouseOver.Play()
				End
			End
			mouseOver = 1
			If MouseHit() Then
				Click()
			Else
				clicked = 0
			End
		Else
			mouseOver = 0
			clicked = 0
		End
	End
End

Class SimpleSlider Extends Sprite
	Field active:Int
	Field dotImage:GameImage
	Field dotX:Int, dotY:Int
	Field value:Int
	Field border:Int=0
	Field borderY:Int=5
	Field useVirtualRes:Bool = False

	Method New(barFile:String, dotFile:String, x:Int, y:int, border:int = 0, name:String="", borderY:int=5, useVirtualRes:Bool = True)
		Self.image = New GameImage
		Self.useVirtualRes = useVirtualRes

		image.Load(game.images.path + barFile, False)
		name = StripAll(barFile.ToUpper())

		dotImage = New GameImage
		dotImage.Load(game.images.path + dotFile, False)
		dotImage.name = StripAll(dotFile.ToUpper())

		Self.x = x
		Self.y = y
		Self.border = border
		Self.borderY = borderY

		Self.SetValue(50)
		Self.dotY = y-3
		Self.active = 1
	End

	Method SetValue:Void(toSet:int)
		value = toSet
		If toSet < 0 Then value = 0
		If toSet > 100 Then value = 100
		Local percent:Float = value/100.0
		dotX = x + border + (percent * (image.w - (border * 2))) - dotImage.w2
	End Method

	Method Update:Int()
		Local change:Int=0
		If active
			Local buffer:int = 10
			Local mx:Int = game.mouseX
			Local my:Int = game.mouseY
			if not useVirtualRes
				mx = MouseX()
				my = MouseY()
			End
			If mx >= x-buffer And mx < x + image.w + buffer And my >= y-borderY And my < y+image.h+borderY
				If MouseDown(MOUSE_LEFT)
					If mx <= x+border
						SetValue(0)
						change = 1
					ElseIf mx >= x+image.w-border
						SetValue(100)
						change = 1
					Else
						Local d:Float = mx - x - border
						Local p:Float = d/(image.w-(border*2))
						SetValue(Round(p*100))
						change = 1
					End
				End
			End
		End
		Return change
	End

	Method Draw:Void()
		If active
			DrawImage(image.image,x,y)
			DrawImage(dotImage.image,dotX,dotY)
		End
	End
End

Import diddy

Class SimpleMenu Extends List<SimpleButton>
	Field x:Float, y:Float
	Field buttonGap:Int = 0
	Field mouseOverName:String = ""
	Field clickedName:String = ""
	Field clearClickedName:Int = 1
	Field nextY:Int = 0
	Field w:Int, h:Int
	Field addGap:Int = 0
	Field soundMouseOver:GameSound
	Field soundClick:GameSound
	Field useVirtualRes:Bool = False

	Method New()
		Error "Please use a different constructor"
	End

	Method New(soundMouseOverFile$, soundClickFile$, x:Int, y:Int, gap:Int, useVirtualRes:Bool)
		Init(soundMouseOverFile, soundClickFile, x, y, gap, useVirtualRes)
	End

	Method Init:Void(soundMouseOverFile:String="", soundClickFile:String="", x:Float, y:Float, gap:Int, useVirtualRes:Bool)
		Self.Clear()
		Self.useVirtualRes = useVirtualRes
		Self.x = x
		Self.y = y
		nextY = y
		Self.buttonGap = gap
		h = 0
		mouseOverName = ""
		clickedName = ""
		addGap = 0
		If soundMouseOverFile<>"" Then
			soundMouseOver = New GameSound
			soundMouseOver.Load(soundMouseOverFile)
		End
		If soundClickFile<>"" Then
			soundClick = New GameSound
			soundClick.Load(soundClickFile)
		End
	End

	Method CalcWidth:Int()
		Local left:Int=10000
		Local right:Int=-10000
		Local b:SimpleButton
		For b = EachIn Self
			If b.x < left Then left = b.x
			If b.x+b.image.w > right Then right = b.x + b.image.w
		Next
		w = right - left
		Return w
	End

	Method CalcHeight:Int()
		Local top:Int=10000
		Local bot:Int=-10000
		Local b:SimpleButton
		For b = EachIn Self
			If b.y < top Then top = b.y
			If b.y+b.image.h > bot Then bot = b.y + b.image.h
		Next
		h = bot - top
		Return h
	End

	Method SetX:Void(thex#)
		CalcLeft()
		Local oldx# = x
		x = thex
		Local diff# = x - oldx
		Local b:SimpleButton
		For b = EachIn Self
			b.MoveBy(diff,0)
		Next
	End

	Method SetY:Void(they#)
		CalcTop()
		Local oldy# = y
		y = they
		Local diff# = y-oldy
		Local b:SimpleButton
		For b = EachIn Self
			b.MoveBy(0,diff)
		Next
	End

	Method CalcLeft:Void()
		x = 10000
		Local b:SimpleButton
		For b = EachIn Self
			If b.x <x Then x = b.x
		Next
	End

	Method CalcTop:Void()
		y = 10000
		Local b:SimpleButton
		For b = EachIn Self
			If b.y < y Then y = b.y
		Next
	End

	Method CentreHoriz:Void()
		CalcWidth()
		if useVirtualRes
			SetX((SCREEN_WIDTH-w)/2)
		Else
			SetX((DEVICE_WIDTH-w)/2)
		End
	End

	Method CentreVert:Void()
		CalcHeight()
		if useVirtualRes
			SetY((SCREEN_HEIGHT-h)/2)
		Else
			SetY((DEVICE_HEIGHT-h)/2)
		End
	End

	Method Centre:Void()
		CentreHoriz()
		CentreVert()
	End

	Method SetMenuAlpha:Void(alpha:Float)
		Local b:SimpleButton
		For b = EachIn Self
			b.alpha = alpha
		Next
	End

	Method AddButton:SimpleButton(buttonImageFile:String, mouseOverFile:String, name:String="")
		Local b:SimpleButton = ProcessAddButton(buttonImageFile, mouseOverFile, name)
		IncreaseHeight(b)
		Return b
	End

	Method IncreaseHeight:Void(b:SimpleButton)
		nextY = nextY + b.image.h + buttonGap

		h = h + b.image.h
		If addGap Then
			h = h + buttonGap
		Else
			addGap = 1
		End
	End

	Method ProcessAddButton:SimpleButton(buttonImageFile:String, mouseOverFile:String, name:String)
		Local b:SimpleButton = New SimpleButton
		b.Load(buttonImageFile, mouseOverFile)
		b.useVirtualRes = Self.useVirtualRes
		If name <> "" Then b.name = name.ToUpper()
		b.CentreX(nextY)
		b.soundMouseOver = soundMouseOver
		b.soundClick = soundClick
		AddLast(b)
		Return b
	End

	Method FindButton:SimpleButton(name:String)
		name = name.ToUpper()
		Local b:SimpleButton
		For b = EachIn Self
			If b.name = name Then Return b
		Next
		Return Null
	End

	Method Clicked:Int(name:String)
		name = name.ToUpper()
		If name = clickedName
			If clearClickedName Then clickedName = ""
			Return 1
		Else
			Return 0
		End
	End

	Method Update:Int()
		If game.screenFade.active
			Return 0
		EndIf
		clickedName = ""
		Local b:SimpleButton
		For b = EachIn Self
			b.Update()
			If b.mouseOver Then mouseOverName = b.name
			If b.clicked Then clickedName = b.name
		Next
		Return 1
	End

	Method Precache:Void()
		For Local b:SimpleButton = EachIn Self
			b.Precache()
		Next
	End

	Method Draw:Void()
		For Local b:SimpleButton = EachIn Self
			b.Draw()
		Next
	End
End

Class SimpleButton Extends Sprite
	Field active:Int = 1
	Field clicked:Int = 0
	Field mouseOver:Int = 0
	Field disabled:Bool = False
	Field soundMouseOver:GameSound
	Field soundClick:GameSound
	Field imageMouseOver:GameImage
	Field useVirtualRes:Bool = False

	Method Precache:Void()
		If image<>null
			Super.Precache()
		End
	End

	Method Draw:Void()
		If active = 0 Then Return
		SetAlpha Self.alpha
		if mouseOver
			DrawImage Self.imageMouseOver.image, x, y
		else
			DrawImage Self.image.image, x, y
		EndIf
		SetAlpha 1
	End

	Method Click:Void()
		If clicked = 0
			clicked = 1
			If soundClick <> null
				soundClick.Play()
			End
		End
	End

	Method CentreX:Void(yCoord:Int)
		if useVirtualRes
			MoveTo((SCREEN_WIDTH-image.w)/2, yCoord)
		Else
			MoveTo((DEVICE_WIDTH-image.w)/2, yCoord)
		End

	End

	Method MoveBy:Void(dx:Float,dy:Float)
		x+=dx
		y+=dy
	End Method

	Method MoveTo:Void(dx:Float,dy:Float)
		x=dx
		y=dy
	End Method

	Method Load:Void(buttonImage:String, mouseOverImage:String = "", soundMouseOverFile:String="", soundClickFile:String="")
		Self.image = New GameImage
		image.Load(game.images.path + buttonImage, False)

		if  mouseOverImage <> ""
			imageMouseOver = New GameImage
			imageMouseOver.Load(game.images.path + mouseOverImage, False)
		End

		name = StripAll(buttonImage.ToUpper())

		If soundMouseOverFile<>"" Then
			soundMouseOver = New GameSound
			soundMouseOver.Load(soundMouseOverFile)
		End
		If soundClickFile<>"" Then
			soundClick = New GameSound
			soundClick.Load(soundClickFile)
		End
	End

	Method Update:Void()
		If active = 0 or disabled Then Return
		Local mx:Int = game.mouseX
		Local my:Int = game.mouseY
		if not useVirtualRes
			mx = MouseX()
			my = MouseY()
		End
		If mx >= x And mx < x+image.w And my >= y And my < y+image.h Then
			If mouseOver = 0
				if soundMouseOver <> null
					soundMouseOver.Play()
				End
			End
			mouseOver = 1
			If MouseHit() Then
				Click()
			Else
				clicked = 0
			End
		Else
			mouseOver = 0
			clicked = 0
		End
	End
End

Class SimpleSlider Extends Sprite
	Field active:Int
	Field dotImage:GameImage
	Field dotX:Int, dotY:Int
	Field value:Int
	Field border:Int=0
	Field borderY:Int=5
	Field useVirtualRes:Bool = False

	Method New(barFile:String, dotFile:String, x:Int, y:int, border:int = 0, name:String="", borderY:int=5, useVirtualRes:Bool = True)
		Self.image = New GameImage
		Self.useVirtualRes = useVirtualRes

		image.Load(game.images.path + barFile, False)
		name = StripAll(barFile.ToUpper())

		dotImage = New GameImage
		dotImage.Load(game.images.path + dotFile, False)
		dotImage.name = StripAll(dotFile.ToUpper())

		Self.x = x
		Self.y = y
		Self.border = border
		Self.borderY = borderY

		Self.SetValue(50)
		Self.dotY = y-3
		Self.active = 1
	End

	Method SetValue:Void(toSet:int)
		value = toSet
		If toSet < 0 Then value = 0
		If toSet > 100 Then value = 100
		Local percent:Float = value/100.0
		dotX = x + border + (percent * (image.w - (border * 2))) - dotImage.w2
	End Method

	Method Update:Int()
		Local change:Int=0
		If active
			Local buffer:int = 10
			Local mx:Int = game.mouseX
			Local my:Int = game.mouseY
			if not useVirtualRes
				mx = MouseX()
				my = MouseY()
			End
			If mx >= x-buffer And mx < x + image.w + buffer And my >= y-borderY And my < y+image.h+borderY
				If MouseDown(MOUSE_LEFT)
					If mx <= x+border
						SetValue(0)
						change = 1
					ElseIf mx >= x+image.w-border
						SetValue(100)
						change = 1
					Else
						Local d:Float = mx - x - border
						Local p:Float = d/(image.w-(border*2))
						SetValue(Round(p*100))
						change = 1
					End
				End
			End
		End
		Return change
	End

	Method Draw:Void()
		If active
			DrawImage(image.image,x,y)
			DrawImage(dotImage.image,dotX,dotY)
		End
	End
End


fix rendering of scales graphics.Sprite

fix rendering of scales graphics.Sprite

Strict

Private

Import align
Import basedisplayobject
Import bono.src.kernel
Import bono.src.utils
Import mojo.graphics

Public

Class Sprite Extends BaseDisplayObject Implements Updateable, Renderable
    Private

    Field currentFrame:Int
    Field frameCount:Int
    Field frameSize:Vector2D
    Field frameTimer:Int
    Field image:Image
    Field renderPos:Vector2D = New Vector2D()
    Field imageName:String
    Field _scale:Vector2D = New Vector2D(1, 1)
    Global cacheImage:StringMap<Image> = New StringMap<Image>()
    Global cacheSize:StringMap<Vector2D> = New StringMap<Vector2D>()

    Public

    Field frameSpeed:Int
    Field loopAnimation:Bool
    Field rotation:Float

    Method New(imageName:String, pos:Vector2D=Null)
        Self.imageName = imageName

        LoadImage()
    End

    Method New(imageName:String, frameSize:Vector2D, frameCount:Int, frameSpeed:Int, pos:Vector2D=Null)
        Self.imageName = imageName
        Self.frameSize = frameSize
        Self.frameCount = frameCount
        Self.frameSpeed = frameSpeed

        LoadImage()
    End

    Method OnRender:Void()
        GetColor().Activate()
        renderPos.Set(GetCenter()).Mul(scale).Add(GetPosition())
        DrawImage(image, renderPos.x, renderPos.y, rotation, scale.x, scale.y, currentFrame)
        GetColor().Deactivate()
    End

    Method OnUpdate:Void(deltaTimer:DeltaTimer)
        If frameCount <= 0 Then Return
        If animationIsDone Then Return

        If frameTimer < frameSpeed
            frameTimer += deltaTimer.frameTime
            Return
        End

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

    Method Restart:Void()
        currentFrame = 0
    End

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

    Method scale:Vector2D() Property
        Return _scale
    End

    Method scale:Void(newScale:Vector2D) Property
        If image Then GetSize().Div(_scale).Mul(newScale)
        _scale = newScale
    End

    Method filepath:String() Property
        Return imageName
    End

    Method DrawImageRect:Void(pos:Vector2D, rectPos:Vector2D, rectSize:Vector2D)
        DrawImageRect(pos.x, pos.y, rectPos.x, rectPos.y, rectSize.x, rectSize.y)
    End

    Method DrawImageRect:Void(x:Float, y:Float, srcX:Float, srcY:Float, srcWidth:Float, srcHeight:Float)
        x += GetCenter().x
        y += GetCenter().y
        graphics.DrawImageRect(image, x, y, srcX, srcY, srcWidth, srcHeight, rotation, scale.x, scale.y, currentFrame)
    End

    Private

    Method CacheSet:Void(name:String, image:Image, size:Vector2D)
        cacheImage.Set(name, image)
        cacheSize.Set(name, size)
    End

    Method CacheGetSize:Vector2D(name:String)
        Return cacheSize.Get(name)
    End

    Method CacheGetImage:Image(name:String)
        Return cacheImage.Get(name)
    End

    Method LoadImage:Void()
        ' Try the cache first ...
        image = CacheGetImage(imageName)
        If image
            SetSize(CacheGetSize(imageName))
        End

        ' ... but handle misses aswell
        If Not image
            If frameSize
                image = graphics.LoadImage(imageName, frameSize.x, frameSize.y, frameCount, Image.MidHandle)
                SetSize(frameSize.Copy())
            Else
                image = graphics.LoadImage(imageName, 1, Image.MidHandle)
                SetSize(New Vector2D(image.Width(), image.Height()))
            End
            CacheSet(imageName, image, GetSize())
        End

        If Not image Then Error("Unable to load: " + imageName)
        GetSize().Mul(scale)
    End
End

Strict

Private

Import align
Import basedisplayobject
Import bono.src.kernel
Import bono.src.utils
Import mojo.graphics

Public

Class Sprite Extends BaseDisplayObject Implements Updateable, Renderable
    Private

    Field currentFrame:Int
    Field frameCount:Int
    Field frameSize:Vector2D
    Field frameTimer:Int
    Field image:Image
    Field renderPos:Vector2D = New Vector2D()
    Field imageName:String
    Field _scale:Vector2D = New Vector2D(1, 1)
    Global cacheImage:StringMap<Image> = New StringMap<Image>()
    Global cacheSize:StringMap<Vector2D> = New StringMap<Vector2D>()

    Public

    Field frameSpeed:Int
    Field loopAnimation:Bool
    Field rotation:Float

    Method New(imageName:String, pos:Vector2D=Null)
        Self.imageName = imageName

        LoadImage()
    End

    Method New(imageName:String, frameSize:Vector2D, frameCount:Int, frameSpeed:Int, pos:Vector2D=Null)
        Self.imageName = imageName
        Self.frameSize = frameSize
        Self.frameCount = frameCount
        Self.frameSpeed = frameSpeed

        LoadImage()
    End

    Method OnRender:Void()
        GetColor().Activate()
        renderPos.Set(GetPosition()).Add(GetCenter())
        DrawImage(image, renderPos.x, renderPos.y, rotation, scale.x, scale.y, currentFrame)
        GetColor().Deactivate()
    End

    Method OnUpdate:Void(deltaTimer:DeltaTimer)
        If frameCount <= 0 Then Return
        If animationIsDone Then Return

        If frameTimer < frameSpeed
            frameTimer += deltaTimer.frameTime
            Return
        End

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

    Method Restart:Void()
        currentFrame = 0
    End

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

    Method scale:Vector2D() Property
        Return _scale
    End

    Method scale:Void(newScale:Vector2D) Property
        If image Then GetSize().Div(_scale).Mul(newScale)
        _scale = newScale
    End

    Method filepath:String() Property
        Return imageName
    End

    Method DrawImageRect:Void(pos:Vector2D, rectPos:Vector2D, rectSize:Vector2D)
        DrawImageRect(pos.x, pos.y, rectPos.x, rectPos.y, rectSize.x, rectSize.y)
    End

    Method DrawImageRect:Void(x:Float, y:Float, srcX:Float, srcY:Float, srcWidth:Float, srcHeight:Float)
        x += GetCenter().x
        y += GetCenter().y
        graphics.DrawImageRect(image, x, y, srcX, srcY, srcWidth, srcHeight, rotation, scale.x, scale.y, currentFrame)
    End

    Private

    Method CacheSet:Void(name:String, image:Image, size:Vector2D)
        cacheImage.Set(name, image)
        cacheSize.Set(name, size)
    End

    Method CacheGetSize:Vector2D(name:String)
        Return cacheSize.Get(name)
    End

    Method CacheGetImage:Image(name:String)
        Return cacheImage.Get(name)
    End

    Method LoadImage:Void()
        ' Try the cache first ...
        image = CacheGetImage(imageName)
        If image
            SetSize(CacheGetSize(imageName))
        End

        ' ... but handle misses aswell
        If Not image
            If frameSize
                image = graphics.LoadImage(imageName, frameSize.x, frameSize.y, frameCount, Image.MidHandle)
                SetSize(frameSize.Copy())
            Else
                image = graphics.LoadImage(imageName, 1, Image.MidHandle)
                SetSize(New Vector2D(image.Width(), image.Height()))
            End
            CacheSet(imageName, image, GetSize())
        End

        If Not image Then Error("Unable to load: " + imageName)
        GetSize().Mul(scale)
    End
End


#3 cameras as property defined

#3 cameras as property defined

#rem
	header:This module contains the FlxBasic class and FlxClass interface.
#end
Strict

Import flxextern
Import flxcamera
Import flxg

#Rem
summary:This is a useful "generic" Flixel object.
Both [a flxobject.monkey.html]FlxObject[/a] and [a flxgroup.monkey.html]FlxGroup[/a] extend this class,
as do the plugins.  Has no size, position or graphical data.
#End
Class FlxBasic

	Global _ActiveCount:Int

	Global _VisibleCount:Int

	Global ClassObject:FlxClass = New FlxBasicClass()

	Global ExistsComparator:FlxBasicComparator = new FlxBasicExistsComparator()

	Global ActiveComparator:FlxBasicComparator = new FlxBasicActiveComparator()

	Global VisibleComparator:FlxBasicComparator = new FlxBasicVisibleComparator()

	Global AliveComparator:FlxBasicComparator = new FlxBasicAliveComparator()

	#Rem
	summary:IDs seem like they could be pretty useful, huh?
	They're not actually used for anything yet though.
	#End
	Field ID:Int

	#Rem
	summary:See details.
	Controls whether [a #Update]Update()[/a] and [a #Draw]Draw()[/a] are automatically called by [a flxstate.monkey.html]FlxState[/a]/[a flxgroup.monkey.html]FlxGroup[/a].
	#End
	Field exists:Bool

	#Rem
	summary:See details.
	Controls whether [a #Update]Update()[/a] is automatically called by [a flxstate.monkey.html]FlxState[/a]/[a flxgroup.monkey.html]FlxGroup[/a].
	#End
	Field active:Bool

	#Rem
	summary:See details.
	Controls whether [a #Draw]Draw()[/a] is automatically called by[a flxstate.monkey.html]FlxState[/a]/[a flxgroup.monkey.html]FlxGroup[/a].
	#End
	Field visible:Bool

	#Rem
	summary:Useful state for many game objects - "dead" (!alive) vs alive.
	[a #Kill]Kill()[/a] and [a #Recive]Revive()[/a] both flip this switch (along with exists, but you can override that).
	#End
	Field alive:Bool

	#Rem
	summary:Setting this to true will prevent the object from appearing when the visual debug mode in the debugger overlay is toggled on.
	#End
	Field ignoreDrawDebug:Bool

	Field _cameras:IntSet

	#Rem
	summary:Instantiate the basic flixel object.
	#End
	Method New()
		ID = -1
		exists = True
		active = True
		visible = True
		alive = True
		ignoreDrawDebug = False
	End Method

	#Rem
	summary:See details.
	Override this function to null out variables or manually call [a #Destroy]Destroy()[/a] on class members if necessary.
	Don't forget to call [b]super.Destroy()[/b]!
	#End
	Method Destroy:Void()
	End Method

	#Rem
	summary:See details.
	Pre-update is called right before [a #Update]Update()[/a] on each object in the game loop.
	#End
	Method PreUpdate:Void()
		_ActiveCount+=1
	End Method

	#Rem
	summary:Override this function to update your class's position and appearance.
	This is where most of your game rules and behavioral code will go.
	#End
	Method Update:Void()
	End Method

	#Rem
	summary:See details.
	Post-update is called right after [a #Update]Update()[/a] on each object in the game loop.
	#End
	Method PostUpdate:Void()
	End Method

	#Rem
	summary:Override this function to control how the object is drawn.
	Overriding [a #Draw]Draw()[/a] is rarely necessary, but can be very useful.
	#End
	Method Draw:Void()
		_VisibleCount+=1
	End Method

	#Rem
	summary:Override this function to draw custom "debug mode" graphics to the specified camera while the debugger's visual mode is toggled on.
	Params:
	[list]
	[*][a flxcamera.monkey.html]camera:FlxCamera[/a] - which camera to draw the debug visuals to.
	[/list]
	#End
	Method DrawDebug:Void(camera:FlxCamera = Null)
	End Method

	#Rem
	summary: Handy function for "killing" game objects.
	Default behavior is to flag them as nonexistent AND dead.
	However, if you want the "corpse" to remain in the game,
	like to animate an effect or whatever, you should override this,
	setting only alive to false, and leaving exists true.
	#End
	Method Kill:Void()
		alive = False
		exists = False
	End Method

	#Rem
	summary:Handy function for bringing game objects "back to life". Just sets alive and exists back to true.
	In practice, this function is most often called by [a flxobject.monkey.html#Reset]FlxObject.Reset()[/a].
	#End
	Method Revive:Void()
		alive = True
		exists = True
	End Method

	Method Cameras:Void(cameras:Int[]) Property
		If (cameras.Length() = 0) Then
			_cameras = Null
			Return
		End If

		If (_cameras = Null) _cameras = New IntSet()

		Local l:Int = cameras.Length()
		Local i:Int = 0

		While (i < l)
			_cameras.Insert(cameras[i])
			i += 1
		Wend
	End Method

	#Rem
	summary:Convert object to readable string name.  Useful for debugging, save games, etc.
	#End
	Method ToString:String()
		Return "FlxBasic"
	End Method

End Class

Interface FlxBasicInvoker

	Method Invoke:Void(object:FlxBasic)

End Interface

Interface FlxBasicSetter

	Method Set:Void(object:FlxBasic, value:Object)

End Interface

Interface FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic,rhs:FlxBasic)

End Interface

Private
Class FlxBasicClass Implements FlxClass

	Method CreateInstance:Object()
		Return New FlxBasic()
	End Method

	Method InstanceOf:Bool(object:Object)
		Return (FlxBasic(object) <> Null)
	End Method

End Class

Class FlxBasicExistsComparator Implements FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic, rhs:FlxBasic)
		If (lhs.exists) Then
			If (rhs.exists) Return 0
			Return 1
		Else
			If (Not rhs.exists) Return 0
			Return -1
		End If
	End Method

End Class

Class FlxBasicActiveComparator Implements FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic, rhs:FlxBasic)
		If (lhs.active) Then
			If (rhs.active) Return 0
			Return 1
		Else
			If (Not rhs.active) Return 0
			Return -1
		End If
	End Method

End Class

Class FlxBasicVisibleComparator Implements FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic, rhs:FlxBasic)
		If (lhs.visible) Then
			If (rhs.visible) Return 0
			Return 1
		Else
			If (Not rhs.visible) Return 0
			Return -1
		End If
	End Method

End Class

Class FlxBasicAliveComparator Implements FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic, rhs:FlxBasic)
		If (lhs.alive) Then
			If (rhs.alive) Return 0
			Return 1
		Else
			If (Not rhs.alive) Return 0
			Return -1
		End If
	End Method

End Class

#Rem
footer:Flixel is an open source game-making library that is completely free for personal or commercial use.
[quote]Copyright: Flixel - 2009-2011 Adam 'Atomic' Saltsman
Copyright: Monkey port - 2011 Arthur 'devolonter' Bikmullin

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

NOTE FROM THE AUTHOR: As far as I know, you only have to include
this license if you are redistributing source code that includes
the Flixel library.  There is no need (or way, afaik) to include
it in your compiled flash games and apps![/quote]
#End

#rem
	header:This module contains the FlxBasic class and FlxClass interface.
#end
Strict

Import flxextern
Import flxcamera
Import flxg

#Rem
summary:This is a useful "generic" Flixel object.
Both [a flxobject.monkey.html]FlxObject[/a] and [a flxgroup.monkey.html]FlxGroup[/a] extend this class,
as do the plugins.  Has no size, position or graphical data.
#End
Class FlxBasic

	Global _ActiveCount:Int

	Global _VisibleCount:Int

	Global ClassObject:FlxClass = New FlxBasicClass()

	Global ExistsComparator:FlxBasicComparator = new FlxBasicExistsComparator()

	Global ActiveComparator:FlxBasicComparator = new FlxBasicActiveComparator()

	Global VisibleComparator:FlxBasicComparator = new FlxBasicVisibleComparator()

	Global AliveComparator:FlxBasicComparator = new FlxBasicAliveComparator()

	#Rem
	summary:IDs seem like they could be pretty useful, huh?
	They're not actually used for anything yet though.
	#End
	Field ID:Int

	#Rem
	summary:See details.
	Controls whether [a #Update]Update()[/a] and [a #Draw]Draw()[/a] are automatically called by [a flxstate.monkey.html]FlxState[/a]/[a flxgroup.monkey.html]FlxGroup[/a].
	#End
	Field exists:Bool

	#Rem
	summary:See details.
	Controls whether [a #Update]Update()[/a] is automatically called by [a flxstate.monkey.html]FlxState[/a]/[a flxgroup.monkey.html]FlxGroup[/a].
	#End
	Field active:Bool

	#Rem
	summary:See details.
	Controls whether [a #Draw]Draw()[/a] is automatically called by[a flxstate.monkey.html]FlxState[/a]/[a flxgroup.monkey.html]FlxGroup[/a].
	#End
	Field visible:Bool

	#Rem
	summary:Useful state for many game objects - "dead" (!alive) vs alive.
	[a #Kill]Kill()[/a] and [a #Recive]Revive()[/a] both flip this switch (along with exists, but you can override that).
	#End
	Field alive:Bool

	#Rem
	summary:Setting this to true will prevent the object from appearing when the visual debug mode in the debugger overlay is toggled on.
	#End
	Field ignoreDrawDebug:Bool

	Field _cameras:IntSet

	#Rem
	summary:Instantiate the basic flixel object.
	#End
	Method New()
		ID = -1
		exists = True
		active = True
		visible = True
		alive = True
		ignoreDrawDebug = False
	End Method

	#Rem
	summary:See details.
	Override this function to null out variables or manually call [a #Destroy]Destroy()[/a] on class members if necessary.
	Don't forget to call [b]super.Destroy()[/b]!
	#End
	Method Destroy:Void()
	End Method

	#Rem
	summary:See details.
	Pre-update is called right before [a #Update]Update()[/a] on each object in the game loop.
	#End
	Method PreUpdate:Void()
		_ActiveCount+=1
	End Method

	#Rem
	summary:Override this function to update your class's position and appearance.
	This is where most of your game rules and behavioral code will go.
	#End
	Method Update:Void()
	End Method

	#Rem
	summary:See details.
	Post-update is called right after [a #Update]Update()[/a] on each object in the game loop.
	#End
	Method PostUpdate:Void()
	End Method

	#Rem
	summary:Override this function to control how the object is drawn.
	Overriding [a #Draw]Draw()[/a] is rarely necessary, but can be very useful.
	#End
	Method Draw:Void()
		_VisibleCount+=1
	End Method

	#Rem
	summary:Override this function to draw custom "debug mode" graphics to the specified camera while the debugger's visual mode is toggled on.
	Params:
	[list]
	[*][a flxcamera.monkey.html]camera:FlxCamera[/a] - which camera to draw the debug visuals to.
	[/list]
	#End
	Method DrawDebug:Void(camera:FlxCamera = Null)
	End Method

	#Rem
	summary: Handy function for "killing" game objects.
	Default behavior is to flag them as nonexistent AND dead.
	However, if you want the "corpse" to remain in the game,
	like to animate an effect or whatever, you should override this,
	setting only alive to false, and leaving exists true.
	#End
	Method Kill:Void()
		alive = False
		exists = False
	End Method

	#Rem
	summary:Handy function for bringing game objects "back to life". Just sets alive and exists back to true.
	In practice, this function is most often called by [a flxobject.monkey.html#Reset]FlxObject.Reset()[/a].
	#End
	Method Revive:Void()
		alive = True
		exists = True
	End Method

	Method Cameras:Void(cameras:Int[])
		If (cameras.Length() = 0) Then
			_cameras = Null
			Return
		End If

		If (_cameras = Null) _cameras = New IntSet()

		Local l:Int = cameras.Length()
		Local i:Int = 0

		While (i < l)
			_cameras.Insert(cameras[i])
			i += 1
		Wend
	End Method

	#Rem
	summary:Convert object to readable string name.  Useful for debugging, save games, etc.
	#End
	Method ToString:String()
		Return "FlxBasic"
	End Method

End Class

Interface FlxBasicInvoker

	Method Invoke:Void(object:FlxBasic)

End Interface

Interface FlxBasicSetter

	Method Set:Void(object:FlxBasic, value:Object)

End Interface

Interface FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic,rhs:FlxBasic)

End Interface

Private
Class FlxBasicClass Implements FlxClass

	Method CreateInstance:Object()
		Return New FlxBasic()
	End Method

	Method InstanceOf:Bool(object:Object)
		Return (FlxBasic(object) <> Null)
	End Method

End Class

Class FlxBasicExistsComparator Implements FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic, rhs:FlxBasic)
		If (lhs.exists) Then
			If (rhs.exists) Return 0
			Return 1
		Else
			If (Not rhs.exists) Return 0
			Return -1
		End If
	End Method

End Class

Class FlxBasicActiveComparator Implements FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic, rhs:FlxBasic)
		If (lhs.active) Then
			If (rhs.active) Return 0
			Return 1
		Else
			If (Not rhs.active) Return 0
			Return -1
		End If
	End Method

End Class

Class FlxBasicVisibleComparator Implements FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic, rhs:FlxBasic)
		If (lhs.visible) Then
			If (rhs.visible) Return 0
			Return 1
		Else
			If (Not rhs.visible) Return 0
			Return -1
		End If
	End Method

End Class

Class FlxBasicAliveComparator Implements FlxBasicComparator

	Method Compare:Int(lhs:FlxBasic, rhs:FlxBasic)
		If (lhs.alive) Then
			If (rhs.alive) Return 0
			Return 1
		Else
			If (Not rhs.alive) Return 0
			Return -1
		End If
	End Method

End Class

#Rem
footer:Flixel is an open source game-making library that is completely free for personal or commercial use.
[quote]Copyright: Flixel - 2009-2011 Adam 'Atomic' Saltsman
Copyright: Monkey port - 2011 Arthur 'devolonter' Bikmullin

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

NOTE FROM THE AUTHOR: As far as I know, you only have to include
this license if you are redistributing source code that includes
the Flixel library.  There is no need (or way, afaik) to include
it in your compiled flash games and apps![/quote]
#End


fixed wrong type definition

fixed wrong type definition

Strict

Private

Import bono
Import bono.vendor.skn3.xml
Import mojo
Import os

Public

Class TexturePacker
    Private

    Field loader:ImageLoader
    Field rootImage:Image
    Field childImages := New StringMap<Image>
    Field xml:XMLDoc
    Field xmlFilename:String

    Public

    Global defaultImageLoader:ImageLoader = New MojoImageLoader()

    Method New()
        Throw New InvalidConstructorException("use New(String)")
    End

    Method New(filename:String, loader:ImageLoader=Null)
        If Not loader Then loader = defaultImageLoader
        Self.loader = loader

        xmlFilename = filename
        LoadXml()
        CheckXml()

        LoadRootImage()
        LoadChildImages()

        FreeXmlRecursive(xml)
        xml = Null
    End

    Method Get:Image(name:String)
        If Not childImages.Contains(name)
            Throw New InvalidArgumentException(
                "There is no sprite named: " + name)
        End

        Return childImages.Get(name)
    End

    Method GetNames:MapKeys<String, Image>()
        Return childImages.Keys()
    End

    Method Count:Int()
        Return childImages.Count()
    End

    Method IsEmpty:Bool()
        Return childImages.IsEmpty()
    End

    Method Contains:Bool(name:String)
        Return childImages.Contains(name)
    End

    Method GetFilename:String()
        Return xmlFilename
    End

    Method GetRootImage:Image()
        Return rootImage
    End

    Private

    Method LoadXml:Void()
        Local content:String = mojo.app.LoadString(xmlFilename)
        If Not content Or content = ""
            Throw New RuntimeException(
                "Unable to load definition from file: " + xmlFilename)
        End

        Local error:XMLError = New XMLError
        xml = ParseXML(content, error)

        If Not xml Or error.error
            Throw New RuntimeException(
                "Unable to parse XML: " + error)
        End
    End

    Method CheckXml:Void()
        If Not xml.HasChildren()
            Throw New InvalidXmlException(
                "Given xml file seems to be empty")
        End

        If xml.name <> "textureatlas"
            Throw New InvalidXmlException(
                "First node must be 'TextureAtlas'")
        End

        If Not xml.HasAttribute("imagepath")
            Throw New InvalidXmlException(
                "Missing attribute 'imagepath' in 'TextureAtlas' node")
        End
    End

    Method LoadRootImage:Void()
        Local file := xml.GetAttribute("imagepath")
        rootImage = loader.LoadImage(ExtractDir(xmlFilename) + "/" + file)

        If xml.HasAttribute("width")
            Assert.AssertEquals(
                rootImage.Width(),
                Int(xml.GetAttribute("width")))
        End

        If xml.HasAttribute("height")
            Assert.AssertEquals(
                rootImage.Height(),
                Int(xml.GetAttribute("height")))
        End
    End

    Method LoadChildImages:Void()
        Local name:String
        Local src:Vector2D = New Vector2D()
        Local size:Vector2D = New Vector2D()

        For Local child:XMLNode = EachIn xml.children
            CheckChildXml(child)

            name = child.GetAttribute("n")
            src.x = Int(child.GetAttribute("x"))
            src.y = Int(child.GetAttribute("y"))
            size.x = Int(child.GetAttribute("w"))
            size.y = Int(child.GetAttribute("h"))

            childImages.Set(
                name,
                rootImage.GrabImage(src.x, src.y, size.x, size.y))
        End
    End

    Method CheckChildXml:Void(child:XMLNode)
        If child.name <> "sprite"
            Throw New InvalidXmlException(
                "Unknown node '" + child.name + "' found")
        End

        For Local attr:String = EachIn ["n", "x", "y", "w", "h"]
            If Not child.HasAttribute(attr)
                Throw New InvalidXmlException(
                    "Missing attribute '" + attr + "' in sprite node")
            End
        End

        For Local attr:String = EachIn ["oX", "oY", "oW", "oH", "r"]
            If child.HasAttribute(attr)
                Throw New InvalidXmlException(
                    "Attribute '" + attr + "' found but not yet implemented")
            End
        End

        If child.HasAttribute("r") And child.GetAttribute("r") <> "y"
            Throw New InvalidXmlException(
                "Invalid value for attribute 'r' in sprite node found")
        End
    End

    Method FreeXmlRecursive:Void(xml:XMLNode)
        If Not xml Then Return

        For Local child:XMLNode = EachIn xml.children
            FreeXmlRecursive(child)
            xml.RemoveChild(child)
        End

        xml.ClearAttributes()
        xml.ClearChildren()
    End
End

Strict

Private

Import bono
Import bono.vendor.skn3.xml
Import mojo
Import os

Public

Class TexturePacker
    Private

    Field loader:ImageLoader
    Field rootImage:Image
    Field childImages := New StringMap<Image>
    Field xml:XMLDoc
    Field xmlFilename:String

    Public

    Global defaultImageLoader:ImageLoader = New MojoImageLoader()

    Method New()
        Throw New InvalidConstructorException("use New(String)")
    End

    Method New(filename:String, loader:ImageLoader=Null)
        If Not loader Then loader = defaultImageLoader
        Self.loader = loader

        xmlFilename = filename
        LoadXml()
        CheckXml()

        LoadRootImage()
        LoadChildImages()

        FreeXmlRecursive(xml)
        xml = Null
    End

    Method Get:Image(name:String)
        If Not childImages.Contains(name)
            Throw New InvalidArgumentException(
                "There is no sprite named: " + name)
        End

        Return childImages.Get(name)
    End

    Method GetNames:MapKeys<String, Sprite>()
        Return childImages.Keys()
    End

    Method Count:Int()
        Return childImages.Count()
    End

    Method IsEmpty:Bool()
        Return childImages.IsEmpty()
    End

    Method Contains:Bool(name:String)
        Return childImages.Contains(name)
    End

    Method GetFilename:String()
        Return xmlFilename
    End

    Method GetRootImage:Image()
        Return rootImage
    End

    Private

    Method LoadXml:Void()
        Local content:String = mojo.app.LoadString(xmlFilename)
        If Not content Or content = ""
            Throw New RuntimeException(
                "Unable to load definition from file: " + xmlFilename)
        End

        Local error:XMLError = New XMLError
        xml = ParseXML(content, error)

        If Not xml Or error.error
            Throw New RuntimeException(
                "Unable to parse XML: " + error)
        End
    End

    Method CheckXml:Void()
        If Not xml.HasChildren()
            Throw New InvalidXmlException(
                "Given xml file seems to be empty")
        End

        If xml.name <> "textureatlas"
            Throw New InvalidXmlException(
                "First node must be 'TextureAtlas'")
        End

        If Not xml.HasAttribute("imagepath")
            Throw New InvalidXmlException(
                "Missing attribute 'imagepath' in 'TextureAtlas' node")
        End
    End

    Method LoadRootImage:Void()
        Local file := xml.GetAttribute("imagepath")
        rootImage = loader.LoadImage(ExtractDir(xmlFilename) + "/" + file)

        If xml.HasAttribute("width")
            Assert.AssertEquals(
                rootImage.Width(),
                Int(xml.GetAttribute("width")))
        End

        If xml.HasAttribute("height")
            Assert.AssertEquals(
                rootImage.Height(),
                Int(xml.GetAttribute("height")))
        End
    End

    Method LoadChildImages:Void()
        Local name:String
        Local src:Vector2D = New Vector2D()
        Local size:Vector2D = New Vector2D()

        For Local child:XMLNode = EachIn xml.children
            CheckChildXml(child)

            name = child.GetAttribute("n")
            src.x = Int(child.GetAttribute("x"))
            src.y = Int(child.GetAttribute("y"))
            size.x = Int(child.GetAttribute("w"))
            size.y = Int(child.GetAttribute("h"))

            childImages.Set(
                name,
                rootImage.GrabImage(src.x, src.y, size.x, size.y))
        End
    End

    Method CheckChildXml:Void(child:XMLNode)
        If child.name <> "sprite"
            Throw New InvalidXmlException(
                "Unknown node '" + child.name + "' found")
        End

        For Local attr:String = EachIn ["n", "x", "y", "w", "h"]
            If Not child.HasAttribute(attr)
                Throw New InvalidXmlException(
                    "Missing attribute '" + attr + "' in sprite node")
            End
        End

        For Local attr:String = EachIn ["oX", "oY", "oW", "oH", "r"]
            If child.HasAttribute(attr)
                Throw New InvalidXmlException(
                    "Attribute '" + attr + "' found but not yet implemented")
            End
        End

        If child.HasAttribute("r") And child.GetAttribute("r") <> "y"
            Throw New InvalidXmlException(
                "Invalid value for attribute 'r' in sprite node found")
        End
    End

    Method FreeXmlRecursive:Void(xml:XMLNode)
        If Not xml Then Return

        For Local child:XMLNode = EachIn xml.children
            FreeXmlRecursive(child)
            xml.RemoveChild(child)
        End

        xml.ClearAttributes()
        xml.ClearChildren()
    End
End


Very minor safety tweak.

Very minor safety tweak.

Strict

Public

' Imports (Public):
Import packet

' Imports (Private):
Private

Import megapacket

Import brl.pool

Public

Class PacketPool<PacketType> Abstract
	' Constructor(s) (Public):
	Method New(PacketSize:Int, PoolSize:Int, FixByteOrder:Bool)
		Self.PacketSize = PacketSize
		Self.FixByteOrder = FixByteOrder

		BuildPool(PoolSize)
	End

	' Constructor(s) (Protected):
	Protected

	Method BuildPool:Void(PoolSize:Int)
		Self.Elements = New Stack<PacketType>()

		For Local I:= 1 To PoolSize
			Self.Elements.Push(GeneratePacket())
		Next

		Return
	End

	Public

	' Methods (Protected):
	Protected

	Method GeneratePacket:PacketType() Abstract

	Public

	' Methods (Public):
	Method Allocate:PacketType()
		Local P:PacketType

		If (Elements.IsEmpty) Then
			P = GeneratePacket()
		Else
			P = Elements.Pop()
		Endif

		P.Obtain()

		Return P
	End

	' The return-value of this command specifies if 'P' was accepted.
	Method Release:Bool(P:PacketType)
		If (P = Null) Then
			Return False
		Endif

		If (P.Release()) Then
			P.Reset()

			Elements.Push(P)

			Return True
		Endif

		' Return the default response.
		Return False
	End

	Method Contains:Bool(P:PacketType)
		Return Elements.Contains(P)
	End

	' Properties (Public):
	Method FixByteOrder:Bool() Property
		Return Self._FixByteOrder
	End

	Method PacketSize:Int() Property
		Return Self._PacketSize
	End

	' Properties (Protected):
	Protected

	Method FixByteOrder:Void(Input:Bool) Property
		Self._FixByteOrder = Input

		Return
	End

	Method PacketSize:Void(Input:Int) Property
		Self._PacketSize = Input

		Return
	End

	Public

	' Fields (Protected):
	Protected

	Field Elements:Stack<PacketType>

	Field _PacketSize:Int

	' Booleans / Flags:
	Field _FixByteOrder:Bool

	Public
End

Class BasicPacketPool Extends PacketPool<Packet> Final
	' Constructor(s):
	Method New(PacketSize:Int, PoolSize:Int, FixByteOrder:Bool)
		' Call the super-class's implementation.
		Super.New(PacketSize, PoolSize, FixByteOrder)

		' Assign the initial pool-size for later use.
		Self._InitialPoolSize = PoolSize
	End

	' Methods (Protected):
	Protected

	Method GeneratePacket:Packet() ' Property
		Return (New Packet(PacketSize, FixByteOrder, False))
	End

	Public

	' Properties (Public):
	Method InitialPoolSize:Int() Property
		Return Self._InitialPoolSize
	End

	' Properties (Protected):
	Protected

	Method InitialPoolSize:Void(Input:Int) Property
		Self._InitialPoolSize = Input

		Return
	End

	Public

	' Fields (Protected):
	Protected

	Field _InitialPoolSize:Int

	Public
End

Class ReliablePacketPool Extends PacketPool<ReliablePacket> Final
	' Constructor(s):
	Method New(PacketSize:Int, PoolSize:Int, FixByteOrder:Bool)
		' Call the super-class's implementation.
		Super.New(PacketSize, PoolSize, FixByteOrder)
	End

	' Methods (Protected):
	Protected

	Method GeneratePacket:ReliablePacket() ' Property
		Return (New ReliablePacket(PacketSize, FixByteOrder, False))
	End

	Public
End

#Rem
	Class MegaPacketPool Extends Pool<MegaPacket> Final
	End
#End

Strict

Public

' Imports (Public):
Import packet

' Imports (Private):
Private

Import megapacket

Import brl.pool

Public

Class PacketPool<PacketType> Abstract
	' Constructor(s) (Public):
	Method New(PacketSize:Int, PoolSize:Int, FixByteOrder:Bool)
		Self.PacketSize = PacketSize
		Self.FixByteOrder = FixByteOrder

		BuildPool(PoolSize)
	End

	' Constructor(s) (Protected):
	Protected

	Method BuildPool:Void(PoolSize:Int)
		Self.Elements = New Stack<PacketType>()

		For Local I:= 1 To PoolSize
			Self.Elements.Push(GeneratePacket())
		Next

		Return
	End

	Public

	' Methods (Protected):
	Protected

	Method GeneratePacket:PacketType() Abstract

	Public

	' Methods (Public):
	Method Allocate:PacketType()
		Local P:PacketType

		If (Elements.IsEmpty) Then
			P = GeneratePacket()
		Else
			P = Elements.Pop()
		Endif

		P.Obtain()

		Return P
	End

	' The return-value of this command specifies if 'P' was accepted.
	Method Release:Bool(P:PacketType)
		If (P.Release()) Then
			P.Reset()

			Elements.Push(P)

			Return True
		Endif

		' Return the default response.
		Return False
	End

	Method Contains:Bool(P:PacketType)
		Return Elements.Contains(P)
	End

	' Properties (Public):
	Method FixByteOrder:Bool() Property
		Return Self._FixByteOrder
	End

	Method PacketSize:Int() Property
		Return Self._PacketSize
	End

	' Properties (Protected):
	Protected

	Method FixByteOrder:Void(Input:Bool) Property
		Self._FixByteOrder = Input

		Return
	End

	Method PacketSize:Void(Input:Int) Property
		Self._PacketSize = Input

		Return
	End

	Public

	' Fields (Protected):
	Protected

	Field Elements:Stack<PacketType>

	Field _PacketSize:Int

	' Booleans / Flags:
	Field _FixByteOrder:Bool

	Public
End

Class BasicPacketPool Extends PacketPool<Packet> Final
	' Constructor(s):
	Method New(PacketSize:Int, PoolSize:Int, FixByteOrder:Bool)
		' Call the super-class's implementation.
		Super.New(PacketSize, PoolSize, FixByteOrder)

		' Assign the initial pool-size for later use.
		Self._InitialPoolSize = PoolSize
	End

	' Methods (Protected):
	Protected

	Method GeneratePacket:Packet() ' Property
		Return (New Packet(PacketSize, FixByteOrder, False))
	End

	Public

	' Properties (Public):
	Method InitialPoolSize:Int() Property
		Return Self._InitialPoolSize
	End

	' Properties (Protected):
	Protected

	Method InitialPoolSize:Void(Input:Int) Property
		Self._InitialPoolSize = Input

		Return
	End

	Public

	' Fields (Protected):
	Protected

	Field _InitialPoolSize:Int

	Public
End

Class ReliablePacketPool Extends PacketPool<ReliablePacket> Final
	' Constructor(s):
	Method New(PacketSize:Int, PoolSize:Int, FixByteOrder:Bool)
		' Call the super-class's implementation.
		Super.New(PacketSize, PoolSize, FixByteOrder)
	End

	' Methods (Protected):
	Protected

	Method GeneratePacket:ReliablePacket() ' Property
		Return (New ReliablePacket(PacketSize, FixByteOrder, False))
	End

	Public
End

#Rem
	Class MegaPacketPool Extends Pool<MegaPacket> Final
	End
#End