Added SetViewPort, Width and Height contents to MonkeyMax

' ***** Start mojo.bmax.bmx ******

Global app:gxtkApp;

Type gxtkApp
	Field ginput:gxtkInput;
	Field gaudio:gxtkAudio;
	Field ggraphics:gxtkGraphics;

	Field dead:Int=0;
	Field suspended:Int=0;
	Field vloading:Int=0;
	Field maxloading:Int=0;
	Field updateRate:Int=0;
	Field nextUpdate:Float=0;
	Field updatePeriod:Float=0;
	Field startMillis:Float=0;

	Method New()' gxtkApp()
		app=Self;
		ggraphics=New gxtkGraphics;
	EndMethod

	Method Setup()
		ginput=New gxtkInput;
		gaudio=New gxtkAudio;

		bb_input_SetInputDevice(ginput);
		bb_audio_SetAudioDevice(gaudio);

		startMillis=BlitzMaxMillisecs()

		SetFrameRate( 0 );

		InvokeOnCreate();
		InvokeOnRender();
		Update()
	EndMethod

	Method Update()
		While Not AppTerminate()
			Local updates:Int = 0

			Local cont:Int = 1
			While (cont)
				nextUpdate:+updatePeriod
				InvokeOnUpdate()
				If Not updatePeriod Then cont = 0
				If nextUpdate>BlitzMaxMillisecs() Then cont = 0
				updates:+1
				If updates = 7 Then
					nextUpdate = BlitzMaxMillisecs()
					cont = 0
				EndIf
			Wend
			InvokeOnRender()
		Wend
	EndMethod

	Method InvokeOnCreate()
		If dead Return
		dead = 1
		OnCreate()
		dead = 0
	EndMethod

	Method InvokeOnUpdate()
		If dead Or suspended Or Not updateRate Or vloading Return
		OnUpdate()
	EndMethod

	Method InvokeOnRender()
		If dead Or suspended Return
		ggraphics.BeginRender()
		If vloading
			OnLoading()
		Else
			OnRender()
		EndIf
		ggraphics.EndRender()
	EndMethod

	Method SetFrameRate( fps:Int )
		If fps
			updatePeriod=1000.0/fps;
			nextUpdate=BlitzMaxMillisecs() +updatePeriod;
		Else
			updatePeriod=0
		EndIf
	EndMethod

	Method LoadString:String( path:String )
		path = "data/" + path;
		Return LoadText( path )
	EndMethod

	' ***** GXTK API *****

	Method GraphicsDevice:gxtkGraphics()
		Return ggraphics
	EndMethod

	Method InputDevice:gxtkInput()
		Return ginput
	EndMethod

	Method AudioDevice:gxtkAudio()
		Return gaudio
	EndMethod

	Method SetUpdateRate:Int(hertz:Int)
		updateRate = hertz
		If Not vloading Then SetFrameRate(updateRate)
		Return 0
	EndMethod

	Method MilliSecs:Int()
		Return BlitzMaxMillisecs() - startMillis
	EndMethod

	Method Loading:Int()
		Return vloading;
	EndMethod

	Method OnCreate:Int()
		Return 0;
	EndMethod

	Method OnUpdate:Int()
		Return 0;
	EndMethod

	Method OnSuspend:Int()
		Return 0;
	EndMethod

	Method OnResume:Int()
		Return 0;
	EndMethod

	Method OnRender:Int()
		Return 0;
	EndMethod

	Method OnLoading:Int()
		Return 0;
	EndMethod
EndType

Type gxtkGraphics
	Field gmode:Int = 1

	Method Mode:Int()
		Return gmode
	EndMethod

	Method Cls:Int(r:Int = 0, g:Int = 0, b:Int = 0)
		BlitzMaxCls(r, g, b)
		Return 0
	EndMethod

	Method LoadSurface:gxtkSurface(path:String)
		Local image:TImage = LoadImage("data/"+path)
		If image Then
			Local gs:gxtkSurface = New gxtkSurface
			gs.setImage(image)
			Return gs
		EndIf
		Return Null
	EndMethod

	Method BeginRender()
	EndMethod

	Method EndRender()
		Flip
	EndMethod

	Method SetColor:Int(r:Int, g:Int, b:Int)
		BlitzMaxSetColor(r, g, b)
		Return 0
	EndMethod

	Method SetAlpha:Int(a:Float)
		BlitzMaxSetAlpha(a)
		Return 0
	EndMethod

	Method SetBlend:Int(blend:Int)
		BlitzMaxSetBlend(blend)
		Return 0
	EndMethod

	Method Width:Int()
		Return GraphicsWidth()
	EndMethod

	Method Height:Int()
		Return GraphicsHeight()
	EndMethod

	Method SetScissor:Int(x:Int, y:Int, w:Int, h:Int)
		SetViewport(x, y, w, h) ' NOT TESTED!
		Return 0
	EndMethod

	Method SetMatrix:Int(ix:Float,iy:Float,jx:Float,jy:Float,tx:Float,ty:Float)
		Local sx:Float = Sqr( (ix*ix) + (jx*jx) )
		Local sy:Float = Sqr( (iy*iy) + (jy*jy) )
		Local rot:Float = -Atan2( jx, ix )
		SetTransform( rot, sx, sy )
		SetOrigin( tx, ty )
		Return 0
	EndMethod

	Method DrawSurface:Int(surface:gxtkSurface,x:Float,y:Float)
		DrawImage(surface.image, x, y, 0)
		Return 0
	EndMethod

	Method DrawSurface2:Int(surface:gxtkSurface,x:Float,y:Float, srcx:Int, srcy:Int, srcw:Int, srch:Int )
		DrawSubImageRect(surface.image, x, y, srcw, srch, srcx, srcy, srcw, srch)
		Return 0
	EndMethod

	Method DrawLine:Int( x1:Float,y1:Float,x2:Float,y2:Float )
		BlitzMaxDrawLine(x1, y1, x2, y2)
		Return 0
	EndMethod

	Method DrawOval:Int( x:Float, y:Float, w:Float, h:Float )
		BlitzMaxDrawOval(x, y, w, h)
		Return 0
	EndMethod

	Method DrawPoly:Int(vertices:Float[])
		BlitzMaxDrawPoly( vertices )
		Return 0
	EndMethod

	Method DrawRect:Int( x:Float, y:Float, w:Float, h:Float )
		BlitzMaxDrawRect(x, y, w, h)
		Return 0
	EndMethod
EndType

Type gxtkInput
	Method MouseX:Float()
		Return BRL.PolledInput.MouseX()
	EndMethod

	Method MouseY:Float()
		Return BRL.PolledInput.MouseY()
	EndMethod

	Method KeyDown:Int( key:Int )
		If( key >= 1 And key <= 3 )
			Return BRL.PolledInput.MouseDown( key )
		EndIf
		Return BRL.PolledInput.KeyDown( key )
	EndMethod

	Method KeyHit:Int( key:Int )
		If( key >= 1 And key <= 3 )
			Return BRL.PolledInput.MouseHit( key )
		EndIf
		Return BRL.PolledInput.KeyHit( key )
	EndMethod

	Method GetChar:Int()
		Return BRL.PolledInput.GetChar()
	EndMethod

	Method JoyX:Int( index:Int )
		Return Pub.FreeJoy.JoyX( index )
	EndMethod

	Method JoyY:Int( index:Int )
		Return Pub.FreeJoy.JoyY( index )
	EndMethod

	Method JoyZ:Int( index:Int )
		Return Pub.FreeJoy.JoyZ( index )
	EndMethod

	Method TouchX:Float( index:Int )
		Return 0
	EndMethod

	Method TouchY:Float( index:Int )
		Return 0
	EndMethod
EndType

Type gxtkChannel
	Field channel:TChannel	' null then not playing
	Field sample:gxtkSample
	Field loops:int
	'Field transform:SoundTransform=new SoundTransform()
	'Field pausepos:Number
	Field state:int

	Method New()
		channel = New TChannel
	EndMethod
EndType

Type gxtkAudio
	Field amusicState:Int = 0;
	Field channels:gxtkChannel[] = New gxtkChannel[33];

	Method New()
		For local i:Int = 0 To 33 - 1
			channels[i] = New gxtkChannel
		Next
	EndMethod

	Method MusicState:Int()
'		If( musicState = 1 And music.isPlaying() = False )
'			musicState = 0;
'		Endif
		Return amusicState;
	EndMethod

	Method PlayMusic:Int( path:String, flags:Int )
		Return 0
	EndMethod

	Method StopMusic()
	EndMethod

	Method StopChannel( channel:Int )
	EndMethod

	Method PlaySample( sound:gxtkSample, channel:Int, flags:Int )
		Local chan:gxtkChannel = channels[channel]

		'If chan.state <> 0 Then chan.channel.Stop() <-- this crashes after the first play!?

		chan.sample = sound
		'chan.loops = flags ? 0x7fffffff : 0;
		'chan.channel = sample.sound.play( 0,chan.loops,chan.transform );
		'chan.channel = sound
		chan.state=1

		PlaySound( sound.sound, chan.channel )
	EndMethod

	Method SetPan( channel:Int, pan:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetPan(pan)
	EndMethod

	Method SetRate( channel:Int, rate:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetRate(rate)
	EndMethod

	Method SetMusicVolume:Int( volume:Float )
		Return 0
	EndMethod

	Method SetVolume:Int( channel:Int, volume:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetVolume(volume)
		Return 0
	EndMethod

	Method LoadSample:gxtkSample( path:String )
		Local sound:TSound = LoadSound("data/"+path)
		If sound Then
			Local gs:gxtkSample = New gxtkSample
			gs.setSound(sound)
			Return gs
		EndIf
		Return Null
	EndMethod
EndType

Type gxtkSample
	Field sound:TSound

	Method setSound(sound:TSound)
		Self.sound = sound
	EndMethod

	Method Discard()
		Self.sound = null
	EndMethod
EndType

Function BlitzMaxDrawPoly:Int(vertices:Float[])
	DrawPoly vertices
	Return 0
EndFunction

Function BlitzMaxDrawRect:Int( x:Float, y:Float, w:Float, h:Float )
	DrawRect x, y, w, h
	Return 0
EndFunction

Function BlitzMaxDrawOval:Int( x:Float, y:Float, w:Float, h:Float )
	DrawOval x, y, w, h
	Return 0
EndFunction

Function BlitzMaxDrawLine:Int( x1:Float,y1:Float,x2:Float,y2:Float )
	DrawLine x1, y1, x2, y2
	Return 0
EndFunction

Function BlitzMaxMillisecs:Int()
	Return MilliSecs()
EndFunction

Function BlitzMaxSetColor(r:Int, g:Int, b:Int)
	SetColor(r, g, b)
EndFunction

Function BlitzMaxSetBlend(blend:Int)
	Select blend
		Case 0
			SetBlend( ALPHABLEND )
		Case 1
			SetBlend( LIGHTBLEND )
	End Select
EndFunction

Function BlitzMaxSetAlpha(a:Float)
	SetAlpha(a)
EndFunction

Function BlitzMaxCls(r:Int = 0, g:Int = 0, b:Int = 0)
	SetClsColor(r, g, b)
	Cls
EndFunction

Type gxtkSurface
	Field w:Int, h:Int
	Field image:TImage

	Method setImage(image:TImage)
		Self.image = image
	EndMethod

	Method Width:Int()
		Return image.Width
	EndMethod

	Method Height:Int()
		Return image.Height
	EndMethod

	Method Discard()
	EndMethod
EndType

' ***** End mojo.bmax.bmx ******

' ***** Start mojo.bmax.bmx ******

Global app:gxtkApp;

Type gxtkApp
	Field ginput:gxtkInput;
	Field gaudio:gxtkAudio;
	Field ggraphics:gxtkGraphics;

	Field dead:Int=0;
	Field suspended:Int=0;
	Field vloading:Int=0;
	Field maxloading:Int=0;
	Field updateRate:Int=0;
	Field nextUpdate:Float=0;
	Field updatePeriod:Float=0;
	Field startMillis:Float=0;

	Method New()' gxtkApp()
		app=Self;
		ggraphics=New gxtkGraphics;
	EndMethod

	Method Setup()
		ginput=New gxtkInput;
		gaudio=New gxtkAudio;

		bb_input_SetInputDevice(ginput);
		bb_audio_SetAudioDevice(gaudio);

		startMillis=BlitzMaxMillisecs()

		SetFrameRate( 0 );

		InvokeOnCreate();
		InvokeOnRender();
		Update()
	EndMethod

	Method Update()
		While Not AppTerminate()
			Local updates:Int = 0

			Local cont:Int = 1
			While (cont)
				nextUpdate:+updatePeriod
				InvokeOnUpdate()
				If Not updatePeriod Then cont = 0
				If nextUpdate>BlitzMaxMillisecs() Then cont = 0
				updates:+1
				If updates = 7 Then
					nextUpdate = BlitzMaxMillisecs()
					cont = 0
				EndIf
			Wend
			InvokeOnRender()
		Wend
	EndMethod

	Method InvokeOnCreate()
		If dead Return
		dead = 1
		OnCreate()
		dead = 0
	EndMethod

	Method InvokeOnUpdate()
		If dead Or suspended Or Not updateRate Or vloading Return
		OnUpdate()
	EndMethod

	Method InvokeOnRender()
		If dead Or suspended Return
		ggraphics.BeginRender()
		If vloading
			OnLoading()
		Else
			OnRender()
		EndIf
		ggraphics.EndRender()
	EndMethod

	Method SetFrameRate( fps:Int )
		If fps
			updatePeriod=1000.0/fps;
			nextUpdate=BlitzMaxMillisecs() +updatePeriod;
		Else
			updatePeriod=0
		EndIf
	EndMethod

	Method LoadString:String( path:String )
		path = "data/" + path;
		Return LoadText( path )
	EndMethod

	' ***** GXTK API *****

	Method GraphicsDevice:gxtkGraphics()
		Return ggraphics
	EndMethod

	Method InputDevice:gxtkInput()
		Return ginput
	EndMethod

	Method AudioDevice:gxtkAudio()
		Return gaudio
	EndMethod

	Method SetUpdateRate:Int(hertz:Int)
		updateRate = hertz
		If Not vloading Then SetFrameRate(updateRate)
		Return 0
	EndMethod

	Method MilliSecs:Int()
		Return BlitzMaxMillisecs() - startMillis
	EndMethod

	Method Loading:Int()
		Return vloading;
	EndMethod

	Method OnCreate:Int()
		Return 0;
	EndMethod

	Method OnUpdate:Int()
		Return 0;
	EndMethod

	Method OnSuspend:Int()
		Return 0;
	EndMethod

	Method OnResume:Int()
		Return 0;
	EndMethod

	Method OnRender:Int()
		Return 0;
	EndMethod

	Method OnLoading:Int()
		Return 0;
	EndMethod
EndType

Type gxtkGraphics
	Field gmode:Int = 1

	Method Mode:Int()
		Return gmode
	EndMethod

	Method Cls:Int(r:Int = 0, g:Int = 0, b:Int = 0)
		BlitzMaxCls(r, g, b)
		Return 0
	EndMethod

	Method LoadSurface:gxtkSurface(path:String)
		Local image:TImage = LoadImage("data/"+path)
		If image Then
			Local gs:gxtkSurface = New gxtkSurface
			gs.setImage(image)
			Return gs
		EndIf
		Return Null
	EndMethod

	Method BeginRender()
	EndMethod

	Method EndRender()
		Flip
	EndMethod

	Method SetColor:Int(r:Int, g:Int, b:Int)
		BlitzMaxSetColor(r, g, b)
		Return 0
	EndMethod

	Method SetAlpha:Int(a:Float)
		BlitzMaxSetAlpha(a)
		Return 0
	EndMethod

	Method SetBlend:Int(blend:Int)
		BlitzMaxSetBlend(blend)
		Return 0
	EndMethod

	Method Width:Int()
		'TO-DO
		Return 800
	EndMethod

	Method Height:Int()
		'TO-DO
		Return 480
	EndMethod

	Method SetScissor:Int(x:Int, y:Int, w:Int, h:Int)
		Return 0
	EndMethod

	Method SetMatrix:Int(ix:Float,iy:Float,jx:Float,jy:Float,tx:Float,ty:Float)
		Local sx:Float = Sqr( (ix*ix) + (jx*jx) )
		Local sy:Float = Sqr( (iy*iy) + (jy*jy) )
		Local rot:Float = -Atan2( jx, ix )
		SetTransform( rot, sx, sy )
		SetOrigin( tx, ty )
		Return 0
	EndMethod

	Method DrawSurface:Int(surface:gxtkSurface,x:Float,y:Float)
		DrawImage(surface.image, x, y, 0)
		Return 0
	EndMethod

	Method DrawSurface2:Int(surface:gxtkSurface,x:Float,y:Float, srcx:Int, srcy:Int, srcw:Int, srch:Int )
		DrawSubImageRect(surface.image, x, y, srcw, srch, srcx, srcy, srcw, srch)
		Return 0
	EndMethod

	Method DrawLine:Int( x1:Float,y1:Float,x2:Float,y2:Float )
		BlitzMaxDrawLine(x1, y1, x2, y2)
		Return 0
	EndMethod

	Method DrawOval:Int( x:Float, y:Float, w:Float, h:Float )
		BlitzMaxDrawOval(x, y, w, h)
		Return 0
	EndMethod

	Method DrawPoly:Int(vertices:Float[])
		BlitzMaxDrawPoly( vertices )
		Return 0
	EndMethod

	Method DrawRect:Int( x:Float, y:Float, w:Float, h:Float )
		BlitzMaxDrawRect(x, y, w, h)
		Return 0
	EndMethod
EndType

Type gxtkInput
	Method MouseX:Float()
		Return BRL.PolledInput.MouseX()
	EndMethod

	Method MouseY:Float()
		Return BRL.PolledInput.MouseY()
	EndMethod

	Method KeyDown:Int( key:Int )
		If( key >= 1 And key <= 3 )
			Return BRL.PolledInput.MouseDown( key )
		EndIf
		Return BRL.PolledInput.KeyDown( key )
	EndMethod

	Method KeyHit:Int( key:Int )
		If( key >= 1 And key <= 3 )
			Return BRL.PolledInput.MouseHit( key )
		EndIf
		Return BRL.PolledInput.KeyHit( key )
	EndMethod

	Method GetChar:Int()
		Return BRL.PolledInput.GetChar()
	EndMethod

	Method JoyX:Int( index:Int )
		Return Pub.FreeJoy.JoyX( index )
	EndMethod

	Method JoyY:Int( index:Int )
		Return Pub.FreeJoy.JoyY( index )
	EndMethod

	Method JoyZ:Int( index:Int )
		Return Pub.FreeJoy.JoyZ( index )
	EndMethod

	Method TouchX:Float( index:Int )
		Return 0
	EndMethod

	Method TouchY:Float( index:Int )
		Return 0
	EndMethod
EndType

Type gxtkChannel
	Field channel:TChannel	' null then not playing
	Field sample:gxtkSample
	Field loops:int
	'Field transform:SoundTransform=new SoundTransform()
	'Field pausepos:Number
	Field state:int

	Method New()
		channel = New TChannel
	EndMethod
EndType

Type gxtkAudio
	Field amusicState:Int = 0;
	Field channels:gxtkChannel[] = New gxtkChannel[33];

	Method New()
		For local i:Int = 0 To 33 - 1
			channels[i] = New gxtkChannel
		Next
	EndMethod

	Method MusicState:Int()
'		If( musicState = 1 And music.isPlaying() = False )
'			musicState = 0;
'		Endif
		Return amusicState;
	EndMethod

	Method PlayMusic:Int( path:String, flags:Int )
		Return 0
	EndMethod

	Method StopMusic()
	EndMethod

	Method StopChannel( channel:Int )
	EndMethod

	Method PlaySample( sound:gxtkSample, channel:Int, flags:Int )
		Local chan:gxtkChannel = channels[channel]

		'If chan.state <> 0 Then chan.channel.Stop() <-- this crashes after the first play!?

		chan.sample = sound
		'chan.loops = flags ? 0x7fffffff : 0;
		'chan.channel = sample.sound.play( 0,chan.loops,chan.transform );
		'chan.channel = sound
		chan.state=1

		PlaySound( sound.sound, chan.channel )
	EndMethod

	Method SetPan( channel:Int, pan:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetPan(pan)
	EndMethod

	Method SetRate( channel:Int, rate:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetRate(rate)
	EndMethod

	Method SetMusicVolume:Int( volume:Float )
		Return 0
	EndMethod

	Method SetVolume:Int( channel:Int, volume:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetVolume(volume)
		Return 0
	EndMethod

	Method LoadSample:gxtkSample( path:String )
		Local sound:TSound = LoadSound("data/"+path)
		If sound Then
			Local gs:gxtkSample = New gxtkSample
			gs.setSound(sound)
			Return gs
		EndIf
		Return Null
	EndMethod
EndType

Type gxtkSample
	Field sound:TSound

	Method setSound(sound:TSound)
		Self.sound = sound
	EndMethod

	Method Discard()
		Self.sound = null
	EndMethod
EndType

Function BlitzMaxDrawPoly:Int(vertices:Float[])
	DrawPoly vertices
	Return 0
EndFunction

Function BlitzMaxDrawRect:Int( x:Float, y:Float, w:Float, h:Float )
	DrawRect x, y, w, h
	Return 0
EndFunction

Function BlitzMaxDrawOval:Int( x:Float, y:Float, w:Float, h:Float )
	DrawOval x, y, w, h
	Return 0
EndFunction

Function BlitzMaxDrawLine:Int( x1:Float,y1:Float,x2:Float,y2:Float )
	DrawLine x1, y1, x2, y2
	Return 0
EndFunction

Function BlitzMaxMillisecs:Int()
	Return MilliSecs()
EndFunction

Function BlitzMaxSetColor(r:Int, g:Int, b:Int)
	SetColor(r, g, b)
EndFunction

Function BlitzMaxSetBlend(blend:Int)
	Select blend
		Case 0
			SetBlend( ALPHABLEND )
		Case 1
			SetBlend( LIGHTBLEND )
	End Select
EndFunction

Function BlitzMaxSetAlpha(a:Float)
	SetAlpha(a)
EndFunction

Function BlitzMaxCls(r:Int = 0, g:Int = 0, b:Int = 0)
	SetClsColor(r, g, b)
	Cls
EndFunction

Type gxtkSurface
	Field w:Int, h:Int
	Field image:TImage

	Method setImage(image:TImage)
		Self.image = image
	EndMethod

	Method Width:Int()
		Return image.Width
	EndMethod

	Method Height:Int()
		Return image.Height
	EndMethod

	Method Discard()
	EndMethod
EndType

' ***** End mojo.bmax.bmx ******


Added a check to see if the TMap that's being passed to TXMLRPC_Response_Data.DebugData() is set

Rem
	bbdoc: Exception for TXMLRPC_Response_Data
End Rem
Type TXMLRPC_Response_Data_Exception Extends TXMLRPC_Exception
End Type

Rem
	bbdoc: Data returned from XML-RPC server
End Rem
Type TXMLRPC_Response_Data
	Rem
		bbdoc: This field contains all data returned from a response. If a response value was given an ID it can be accessed by its ID. If not it can be accessed with an ID of 0..inf
	End Rem
	Field data:TMap

	Rem
		bbdoc: Create a TXMLRPC_Response_Data object by passing the response XML message and the output options.
	End Rem
	Method Create:TXMLRPC_Response_Data(xmlMessage:String, options:Byte Ptr)
		Local message:Byte Ptr = xmlMessage.ToCString()
		Local request:Byte Ptr = XMLRPC_REQUEST_FromXML(message, 0, options)

		Local el:Byte Ptr = XMLRPC_RequestGetData(request)

		Self.data = TXMLRPC_Response_Data.IterateVector(el)

		MemFree(message)

		Local freeIo:Int = 1
		'Apparently a unhandled memory exception error gets thrown when
		'xmlMessage only contained 1 value and XMLRPC_RequestFree also
		'frees the response data.
		If Not XMLRPC_VectorRewind(el) Then freeIo = 0

		XMLRPC_RequestFree(request, freeIo)

		Return Self
	End Method

	Rem
		bbdoc: Iterate over an XMLRPC vector and add it to a TMap. Private function
	End Rem
	Function IterateVector:TMap(el:Byte Ptr)
		Local data:TMap = New TMap

		If el
			'Rewind vector
			Local itr:Byte Ptr = XMLRPC_VectorRewind(el)
			If Not itr
				Local cStr:Byte Ptr = XMLRPC_GetValueID(el)
				Local id:String = "0"
				If cStr
					id = String.FromCString(cStr)
					XMLRPC_Free(cstr)
				End If

				data.Insert(id, TXMLRPC_Value_Abstract.XMLRPC_To_BlizMax(el))
			Else
				Local dataCounter:Int = 0
				While itr
					Local cStr:Byte Ptr = XMLRPC_GetValueID(itr)
					Local id:String = String.FromCString(cStr)

					If id.Length = 0
						id = String.FromInt(dataCounter)
						dataCounter:+1
					End If

					data.Insert(id, TXMLRPC_Value_Abstract.XMLRPC_To_BlizMax(itr))
					XMLRPC_Free(cStr)

					'Next element
					itr = XMLRPC_VectorNext(el)
				Wend
			End If
		End If

		Return data
	End Function

	Rem
		bbdoc: Helper function to show what's inside the map
	End Rem
	Function DebugData:String(map:TMap, pad:Int Var)
		Local str:String
		Local prefix:String
		For Local i:Int = 0 To pad
			prefix:+"-"
		Next

		If map
			For Local val:TXMLRPC_Value_Abstract = EachIn map.Values()
				str:+prefix + " " + TTypeId.ForObject(val).Name() + " (" + val.name + ") = " + val.ToString() + "~n"
				If TXMLRPC_Value_Collection(val)
					pad:+1
					str:+TXMLRPC_Response_Data.DebugData(TXMLRPC_Value_Collection(val).data, pad) + "~n"
					pad:-1
				End If
			Next
		End If
		Return str
	End Function
End Type

Rem
	bbdoc: Exception for TXMLRPC_Response_Data
End Rem
Type TXMLRPC_Response_Data_Exception Extends TXMLRPC_Exception
End Type

Rem
	bbdoc: Data returned from XML-RPC server
End Rem
Type TXMLRPC_Response_Data
	Rem
		bbdoc: This field contains all data returned from a response. If a response value was given an ID it can be accessed by its ID. If not it can be accessed with an ID of 0..inf
	End Rem
	Field data:TMap

	Rem
		bbdoc: Create a TXMLRPC_Response_Data object by passing the response XML message and the output options.
	End Rem
	Method Create:TXMLRPC_Response_Data(xmlMessage:String, options:Byte Ptr)
		Local message:Byte Ptr = xmlMessage.ToCString()
		Local request:Byte Ptr = XMLRPC_REQUEST_FromXML(message, 0, options)

		Local el:Byte Ptr = XMLRPC_RequestGetData(request)

		Self.data = TXMLRPC_Response_Data.IterateVector(el)

		MemFree(message)

		Local freeIo:Int = 1
		'Apparently a unhandled memory exception error gets thrown when
		'xmlMessage only contained 1 value and XMLRPC_RequestFree also
		'frees the response data.
		If Not XMLRPC_VectorRewind(el) Then freeIo = 0

		XMLRPC_RequestFree(request, freeIo)

		Return Self
	End Method

	Rem
		bbdoc: Iterate over an XMLRPC vector and add it to a TMap. Private function
	End Rem
	Function IterateVector:TMap(el:Byte Ptr)
		Local data:TMap = New TMap

		If el
			'Rewind vector
			Local itr:Byte Ptr = XMLRPC_VectorRewind(el)
			If Not itr
				Local cStr:Byte Ptr = XMLRPC_GetValueID(el)
				Local id:String = "0"
				If cStr
					id = String.FromCString(cStr)
					XMLRPC_Free(cstr)
				End If

				data.Insert(id, TXMLRPC_Value_Abstract.XMLRPC_To_BlizMax(el))
			Else
				Local dataCounter:Int = 0
				While itr
					Local cStr:Byte Ptr = XMLRPC_GetValueID(itr)
					Local id:String = String.FromCString(cStr)

					If id.Length = 0
						id = String.FromInt(dataCounter)
						dataCounter:+1
					End If

					data.Insert(id, TXMLRPC_Value_Abstract.XMLRPC_To_BlizMax(itr))
					XMLRPC_Free(cStr)

					'Next element
					itr = XMLRPC_VectorNext(el)
				Wend
			End If
		End If

		Return data
	End Function

	Rem
		bbdoc: Helper function to show what's inside the map
	End Rem
	Function DebugData:String(map:TMap, pad:Int Var)
		Local str:String
		Local prefix:String
		For Local i:Int = 0 To pad
			prefix:+"-"
		Next

		For Local val:TXMLRPC_Value_Abstract = EachIn map.Values()
			str:+prefix + " " + TTypeId.ForObject(val).Name() + " (" + val.name + ") = " + val.ToString() + "~n"
			If TXMLRPC_Value_Collection(val)
				pad:+1
				str:+TXMLRPC_Response_Data.DebugData(TXMLRPC_Value_Collection(val).data, pad) + "~n"
				pad:-1
			End If
		Next
		Return str
	End Function
End Type


explicit access to _ENV

explicit access to _ENV

' Blue Moon
' FOLD delegates to analyze semantics (build function and variable tables, check for semantic errors)

' this file is a Private Include

'SuperStrict

'Import "blueerror.bmx"
'Import "TFold.bmx"

' this module folds through the AST to extract function bodies and resolve references to variables and labels
' local variables are replaced by references to their location in the function's local table (not their stack
' position, which they hold internally - two variables on the same stack pos may not be identical yet)
' closure variables are replaced by references to their location in the function's closure table

Type SemanticFold
	Field funs:TList, _ft:TList, fcount:Int, tbl:TMap

	Function Make:SemanticFold(root:Node, tbl:NodeTable)
		Local f:SemanticFold = New SemanticFold
		f.funs = CreateList() ; f._ft = CreateList() ; f.tbl = tbl.leaves
		FunDef.Toplevel(root, f)
		Return f
	End Function
	Function Close(semf:SemanticFold, r:Node)
		Local toplevel:FunDef = FunDef(semf.funs.First())
		toplevel.Close(Rose.Make("FuncBody", [Node(Rose.Make("NameList", Null, Null)), r], ["param", "body"]), semf)
	End Function

	Method FuncBody:Node(n:Rose)	'Add function definition to global list
		Local f:FunDef = FunDef.Make(n, Self), params:Rose = Rose(n.arg[0]), pa:Node[] = params.arg[..]
		If pa Then f.hasVarargs = (pa[pa.Length - 1].key = "dotdotdot")
		f.vars = New VarDef[pa.Length]
		For Local p:Int = 0 Until pa.Length	'add parameters
			Local par:Leaf = Leaf(pa[p])
			pa[p] = Leaf.Make("param", par.val)
			If par.key = "name"
				f.vars[p] = f.AddVar(par, Self)
			Else	'drop the ...
				pa = pa[..pa.Length - 1] ; f.vars = f.vars[..pa.Length] ; Exit
			EndIf
		Next
		f.pcount = pa.Length
		Return Rose.Make(n.key, [Node(Rose.Make(params.key, pa)), n.arg[1]], n.id)
	End Method
	Method FuncBody2:Node(n:Rose)	'extract body and pop env
		Local f:FunDef = FunDef(_ft.RemoveFirst()), env:FunDef = FunDef(_ft.First())
		f.Close(n, Self)
		For Local c:VarDef = EachIn f.clos
			If Not env._vt.Contains(c) Then env.AddClos(c)	'make sure all closures are one-level at most
		Next
		Return RefLeaf.Make("funref", NumToStr(f.id))
	End Method

	Method Block:Node(n:Rose)	'enter a scope level
		Local f:FunDef = FunDef(_ft.First()) ; f._ev.AddFirst n
		Return n
	End Method
	Method Block2:Node(n:Rose)	'exit a scope level
		Local f:FunDef = FunDef(_ft.First()), n2:Node = Node(f._ev.RemoveFirst()) ; f.CloseBlock n2, Self	'note that n itself cannot be tested for equality
		Return n
	End Method

	Method RepeatS:Node(n:Rose)	'repeat scope extends to the end of the test, need to put that inside it (never need to remove it, either)
		Local blk:Rose = Rose.Make(n.arg[0].key, Rose(n.arg[0]).arg + [n.arg[1]])
		Return Rose.Make(n.key, [blk])
	End Method

	'for loops are now replaced during normalization
'	Method ForN:Node(n:Rose) { ForIn }	'for loops create scope blocks with variables
'		Local blk:Node = n.Get("block")
'		Local f:FunDef = FunDef(_ft.First()) ; f._ev.AddFirst blk
'		If n.key = "ForN"
'			Local arg:Node[] = n.arg[..], v:VarDef = f.AddVar(Leaf(arg[0]), Self)
'			arg[0] = RefLeaf.Make("l_varref", NumToStr(v.uid))
'			n = Rose.Make(n.key, arg, n.id)
'		Else
'			Local arg:Node[] = Rose(n.arg[0]).arg[..]
'			For Local i:Int = 0 Until arg.Length
'				Local v:VarDef = f.AddVar(Leaf(arg[i]), Self)
'				arg[i] = RefLeaf.Make("l_varref", NumToStr(v.uid))
'			Next
'			n = Rose.Make(n.key, [Node(Rose.Make(n.arg[0].key, arg, Null))] + n.arg[1..], n.id)
'		EndIf
'		Return n
'	End Method
'	Method ForN2:Node(n:Rose) { ForIn2 }	'pop the scope
'		Local f:FunDef = FunDef(_ft.First()) ; f._ev.RemoveFirst()
'		Return n
'	End Method

	Method LocalVar:Node(n:Rose)	'prevent declared names from getting in the way
		Local vars:Node[] = Rose(n.arg[0]).arg[..]
		For Local i:Int = 0 Until vars.Length
			Local t:Object = tbl.ValueForKey(vars[i]) ; tbl.Remove(vars[i])	'need to replace nodes in the err table if they're still potential error points
			vars[i] = Leaf.Make("lname", Leaf(vars[i]).val)
			tbl.Insert(vars[i], t)
		Next
		Return Rose.Make(n.key, [Node(Rose.Make("VarList", vars)), n.arg[1]], n.id)
	End Method
	Method LocalVar2:Node(n:Rose)	'define vars on the way back up
		Local vars0:Node[] = Rose(n.arg[0]).arg, vars:Node[vars0.Length], vr:Node = Rose.Make("VarList", vars)
		For Local i:Int = 0 Until vars.Length
			Local v:VarDef = FunDef(_ft.First()).AddVar(Leaf(vars0[i]), Self)	'notice we only add the name on the way back up
			vars[vars.Length - (i + 1)] = RefLeaf.Make("l_varref", NumToStr(v.uid))	'proceed backward to reflect stack discipline
		Next
		Return Rose.Make(n.key, [n.arg[1], vr], ["vals", "vars"])	'swap vars and vals so vals are evaluated first
	End Method

	Method Assgt2:Node(n:Rose)	'convert lvalue keys to indicate different action
		Local vars0:Node[] = Rose(n.arg[0]).arg, vars:Node[vars0.Length], vr:Node = Rose.Make("VarList", vars)
		For Local v:Int = 0 Until vars0.Length
			vars0[v].key = "l_" + vars0[v].key ; vars[vars.Length - (v + 1)] = vars0[v]
		Next
		Return Rose.Make(n.key, [n.arg[1], vr], ["vals", "vars"])	'swap, as above
	End Method

	Method name:Node(n:Leaf)	'var ref
		Function getvar:VarDef(_ft:TList, n:String)
			For Local f:FunDef = EachIn _ft
				For Local v:VarDef = EachIn f._vt
					If v.name = n
						If f <> _ft.First() Then v.isClosure = True
						Return v
					EndIf
				Next
				For Local v:VarDef = EachIn f._ct
					If v.name = n Then Return v
				Next
			Next
		End Function
		Local v:VarDef = getvar(_ft, n.val), f:FunDef = FunDef(_ft.First()) ; If v
			If v.isClosure
				Local id:Int = f.AddClos(v)	'add to the list of closure vars to retain
				Return RefLeaf.Make("closref", NumToStr(id))
			Else
				Return RefLeaf.Make("varref", NumToStr(v.uid))
			EndIf
		Else	'free, i.e. _ENV.n
			v = getvar(_ft, "_ENV")
			Local id:Int = f.AddClos(v), env:Node = RefLeaf.Make("closref", NumToStr(id)), name:Node = Leaf.Make("fname", n.val)
			Return Rose.Make("getfield", [env, name], Null)
		EndIf
	End Method
	Method fcall:Node(n:Rose) { mcall pfcall pmcall tfcall tmcall va_fcall va_mcall }	'count arguments to function calls
		Local ac:Int
		Select n.key
			Case "fcall", "pfcall", "tfcall", "va_fcall"
				ac = Rose(n.arg[1]).arg.Length
			Default	'*mcall
				ac = Rose(Rose(n.arg[1]).arg[1]).arg.Length
		End Select
		Return Rose.Make(n.key, n.arg + [Leaf.Make("acount", ac)], Null)
	End Method

	Method Label:Node(n:Leaf) { GotoS }	'jump or dest
		Local f:FunDef = FunDef(_ft.First()), val:Int
		If n.key = "Label" Then val = f.AddLabel(n, Self) Else val = f.AddGoto(n)
		Return RefLeaf.Make(n.key, NumToStr(val))
	End Method

	Method ReturnS2:Node(n:Rose)	'detect and mark tail calls (on the way up to give the call a chance to process)
		If n.arg.Length = 1
			Local k:String = n.arg[0].key, c:Rose = Rose(n.arg[0])
			If k = "fcall" Or k = "mcall"
				Return Rose.Make("t" + k, c.arg, c.id)
			ElseIf k = "dotdotdot"
				Return Leaf.Make("return_va", "...")
			EndIf
		EndIf
		Return n
	End Method
End Type

Type FunDef
	Field n:Node, id:Int, env:FunDef
	Field vars:VarDef[], clos:VarDef[], pcount:Int, stksz:Int, vmax:Int, vcount:Int, hasVarargs:Int
	Field _ev:TList, _vt:TList, _ct:TList, _lt:TList, _gt:TList

	Function Make:FunDef(blk:Node, in:SemanticFold)
		Local f:FunDef = New FunDef
		f.env = FunDef(in._ft.First())
		f._vt = CreateList() ; f._ev = CreateList() ; f._ct = CreateList() ; f._lt = CreateList() ; f._gt = CreateList()
		in.funs.AddLast f ; in._ft.AddFirst f
		f.id = in.fcount ; in.fcount :+ 1
		Return f
	End Function
	Function Toplevel:FunDef(root:Node, in:SemanticFold)	'create a chunk toplevel as a function whose sole parameter is _ENV
		Local f:FunDef = FunDef.Make(root, in), env:VarDef = VarDef.Make("_ENV", Null, 0, 0, -1)
		env.isClosure = True ; f._ct.AddLast env ; Return f
	End Function
	Method AddVar:VarDef(n:Leaf, in:SemanticFold)
		Local blk:Node = Node(_ev.First()), v:VarDef
		For v = EachIn _vt
			If v.blk <> blk Then Exit	'the error here doesn't actually apply in standard Lua
		'	If v.name = n.val Then Throw CompileError.Make(TToken(in.tbl.ValueForKey(n)), "duplicate local variable '" + n.val + "'")
		Next
		v = VarDef.Make(n.val, blk, stksz, vcount, id) ; vcount :+ 1
		_vt.AddFirst v ; stksz :+ 1 ; Return v
	End Method
	Method AddClos:Int(v:VarDef)
		Local c:Int
		For Local e:VarDef = EachIn _ct
			If v = e Then Return c Else c :+ 1
		Next
		_ct.AddLast v ; Return c
	End Method
	Method AddLabel:Int(n:Leaf, in:SemanticFold)
		Local l:LabelDef = New LabelDef ; l.name = n.val ; l.depth = _ev.Count() ; l.varsvisible = stksz
		For Local ln:LabelDef = EachIn _lt
			If ln.depth = l.depth And ln.name = l.name Then Throw BlueCompileError.Make(TToken(in.tbl.ValueForKey(n)), "duplicate label '::" + n.val + "::'")
		Next
		_lt.AddLast l ; Return _lt.Count() - 1
	End Method
	Method AddGoto:Int(n:Leaf)
		Local g:GotoDef = New GotoDef ; g.name = n.val ; g.depth = _ev.Count() ; g.varsvisible = stksz ; g._n = n
		_gt.AddLast g ; Return _gt.Count() - 1
	End Method
	Method ResolveGotos(in:SemanticFold)
		Function cmp:Int(l:Object, r:Object)
			If LabelDef(l).depth > LabelDef(r).depth Return 1 ElseIf LabelDef(l).depth < LabelDef(r).depth Return -1 Else Return 0
		End Function
		_lt.Sort True, cmp
		For Local g:GotoDef = EachIn _gt
			Local l:LabelDef = Null
			For Local _l:LabelDef = EachIn _lt
				If g.depth < _l.depth Then Exit
				If g.name = _l.name Then l = _l ; Exit
			Next
			If l
				g.tgt = l ; g._n = Null
			Else
				Throw BlueCompileError.Make(TToken(in.tbl.ValueForKey(g._n)), "label '::" + g._n.val + "::' not visible in scope")
			EndIf
		Next
	End Method
	Method CloseBlock(n:Node, in:SemanticFold)
		vmax = Max(vmax, stksz)
		Local c:Int = 0, oldLen:Int = vars.Length
		For Local v:VarDef = EachIn _vt
			If v.blk <> n Then Exit
			c :+ 1 ; v.blk = Null	'noretain
		Next
		vars = New VarDef[c] + vars
		For Local i:Int = 0 Until c
			vars[i] = VarDef(_vt.RemoveFirst())	'pop contained vars
		Next
		stksz :- c
	End Method
	Method Close(_n:Node, in:SemanticFold)
		n = _n ; clos = VarDef[](_ct.ToArray()) ; ResolveGotos in
		Local v2:VarDef[] = New VarDef[vars.Length]		'vars aren't in the right order due to params, sort by uid
		For Local vi:Int = 0 Until v2.Length
			Local v:VarDef = vars[vi] ; v2[v.uid] = v
		Next
		vars = v2
		_vt = Null ; _ev = Null ; _ct = Null '; _lt = Null ; _gt = Null
	End Method
End Type

Type VarDef
	Field name:String, blk:Node, isClosure:Int, stkpos:Int, uid:Int, fid:Int
	Function Make:VarDef(n:String, b:Node, stkpos:Int, uid:Int, fid:Int)
		Local v:VarDef = New VarDef ; v.name = n ; v.stkpos = stkpos ; v.uid = uid ; v.fid = fid ; v.blk = b ; Return v
	End Function
End Type

Type LabelDef
	Field name:String, depth:Int, varsvisible:Int, insl:TLink, insc:Int = -1
End Type

Type GotoDef
	Field name:String, depth:Int, varsvisible:Int, _n:Leaf, tgt:LabelDef', op:Object
End Type

Type RefLeaf Extends Leaf
	Function Make:Leaf(key:String, val:String)
		Local l:Leaf = New RefLeaf
		l.key = key ; l.val = val
		Return l
	End Function
	Method ToString:String()
		Return "{ '#" + StrToNum(val) + "' : " + key + " }"
	End Method
End Type
Function StrToNum:Int(s:String)
	Return s[0] | (s[1] Shl 8) | (s[2] Shl 16) | (s[3] Shl 24)
End Function
Function NumToStr:String(n:Int)
	Return Chr(n & $FF) + Chr((n & $FF00) Shr 8) + Chr((n & $FF0000) Shr 16) + Chr((n & $FF000000) Shr 24)
End Function

' Blue Moon
' FOLD delegates to analyze semantics (build function and variable tables, check for semantic errors)

' this file is a Private Include

'SuperStrict

'Import "blueerror.bmx"
'Import "TFold.bmx"

' this module folds through the AST to extract function bodies and resolve references to variables and labels
' local variables are replaced by references to their location in the function's local table (not their stack
' position, which they hold internally - two variables on the same stack pos may not be identical yet)
' closure variables are replaced by references to their location in the function's closure table

Type SemanticFold
	Field funs:TList, _ft:TList, fcount:Int, tbl:TMap

	Function Make:SemanticFold(root:Node, tbl:NodeTable)
		Local f:SemanticFold = New SemanticFold
		f.funs = CreateList() ; f._ft = CreateList() ; f.tbl = tbl.leaves
		FunDef.Toplevel(root, f)
		Return f
	End Function
	Function Close(semf:SemanticFold, r:Node)
		Local toplevel:FunDef = FunDef(semf.funs.First())
		toplevel.Close(Rose.Make("FuncBody", [Node(Rose.Make("NameList", Null, Null)), r], ["param", "body"]), semf)
	End Function

	Method FuncBody:Node(n:Rose)	'Add function definition to global list
		Local f:FunDef = FunDef.Make(n, Self), params:Rose = Rose(n.arg[0]), pa:Node[] = params.arg[..]
		If pa Then f.hasVarargs = (pa[pa.Length - 1].key = "dotdotdot")
		f.vars = New VarDef[pa.Length]
		For Local p:Int = 0 Until pa.Length	'add parameters
			Local par:Leaf = Leaf(pa[p])
			pa[p] = Leaf.Make("param", par.val)
			If par.key = "name"
				f.vars[p] = f.AddVar(par, Self)
			Else	'drop the ...
				pa = pa[..pa.Length - 1] ; f.vars = f.vars[..pa.Length] ; Exit
			EndIf
		Next
		f.pcount = pa.Length
		Return Rose.Make(n.key, [Node(Rose.Make(params.key, pa)), n.arg[1]], n.id)
	End Method
	Method FuncBody2:Node(n:Rose)	'extract body and pop env
		Local f:FunDef = FunDef(_ft.RemoveFirst()), env:FunDef = FunDef(_ft.First())
		f.Close(n, Self)
		For Local c:VarDef = EachIn f.clos
			If Not env._vt.Contains(c) Then env.AddClos(c)	'make sure all closures are one-level at most
		Next
		Return RefLeaf.Make("funref", NumToStr(f.id))
	End Method

	Method Block:Node(n:Rose)	'enter a scope level
		Local f:FunDef = FunDef(_ft.First()) ; f._ev.AddFirst n
		Return n
	End Method
	Method Block2:Node(n:Rose)	'exit a scope level
		Local f:FunDef = FunDef(_ft.First()), n2:Node = Node(f._ev.RemoveFirst()) ; f.CloseBlock n2, Self	'note that n itself cannot be tested for equality
		Return n
	End Method

	Method RepeatS:Node(n:Rose)	'repeat scope extends to the end of the test, need to put that inside it (never need to remove it, either)
		Local blk:Rose = Rose.Make(n.arg[0].key, Rose(n.arg[0]).arg + [n.arg[1]])
		Return Rose.Make(n.key, [blk])
	End Method

	'for loops are now replaced during normalization
'	Method ForN:Node(n:Rose) { ForIn }	'for loops create scope blocks with variables
'		Local blk:Node = n.Get("block")
'		Local f:FunDef = FunDef(_ft.First()) ; f._ev.AddFirst blk
'		If n.key = "ForN"
'			Local arg:Node[] = n.arg[..], v:VarDef = f.AddVar(Leaf(arg[0]), Self)
'			arg[0] = RefLeaf.Make("l_varref", NumToStr(v.uid))
'			n = Rose.Make(n.key, arg, n.id)
'		Else
'			Local arg:Node[] = Rose(n.arg[0]).arg[..]
'			For Local i:Int = 0 Until arg.Length
'				Local v:VarDef = f.AddVar(Leaf(arg[i]), Self)
'				arg[i] = RefLeaf.Make("l_varref", NumToStr(v.uid))
'			Next
'			n = Rose.Make(n.key, [Node(Rose.Make(n.arg[0].key, arg, Null))] + n.arg[1..], n.id)
'		EndIf
'		Return n
'	End Method
'	Method ForN2:Node(n:Rose) { ForIn2 }	'pop the scope
'		Local f:FunDef = FunDef(_ft.First()) ; f._ev.RemoveFirst()
'		Return n
'	End Method

	Method LocalVar:Node(n:Rose)	'prevent declared names from getting in the way
		Local vars:Node[] = Rose(n.arg[0]).arg[..]
		For Local i:Int = 0 Until vars.Length
			Local t:Object = tbl.ValueForKey(vars[i]) ; tbl.Remove(vars[i])	'need to replace nodes in the err table if they're still potential error points
			vars[i] = Leaf.Make("lname", Leaf(vars[i]).val)
			tbl.Insert(vars[i], t)
		Next
		Return Rose.Make(n.key, [Node(Rose.Make("VarList", vars)), n.arg[1]], n.id)
	End Method
	Method LocalVar2:Node(n:Rose)	'define vars on the way back up
		Local vars0:Node[] = Rose(n.arg[0]).arg, vars:Node[vars0.Length], vr:Node = Rose.Make("VarList", vars)
		For Local i:Int = 0 Until vars.Length
			Local v:VarDef = FunDef(_ft.First()).AddVar(Leaf(vars0[i]), Self)	'notice we only add the name on the way back up
			vars[vars.Length - (i + 1)] = RefLeaf.Make("l_varref", NumToStr(v.uid))	'proceed backward to reflect stack discipline
		Next
		Return Rose.Make(n.key, [n.arg[1], vr], ["vals", "vars"])	'swap vars and vals so vals are evaluated first
	End Method

	Method Assgt2:Node(n:Rose)	'convert lvalue keys to indicate different action
		Local vars0:Node[] = Rose(n.arg[0]).arg, vars:Node[vars0.Length], vr:Node = Rose.Make("VarList", vars)
		For Local v:Int = 0 Until vars0.Length
			vars0[v].key = "l_" + vars0[v].key ; vars[vars.Length - (v + 1)] = vars0[v]
		Next
		Return Rose.Make(n.key, [n.arg[1], vr], ["vals", "vars"])	'swap, as above
	End Method

	Method name:Node(n:Leaf)	'var ref
		Function getvar:VarDef(_ft:TList, n:String)
			For Local f:FunDef = EachIn _ft
				For Local v:VarDef = EachIn f._vt
					If v.name = n
						If f <> _ft.First() Then v.isClosure = True
						Return v
					EndIf
				Next
				For Local v:VarDef = EachIn f._ct
					If v.name = n Then Return v
				Next
			Next
		End Function
		Local v:VarDef = getvar(_ft, n.val), f:FunDef = FunDef(_ft.First()) ; If v
			If v.isClosure
				Local id:Int = f.AddClos(v)	'add to the list of closure vars to retain
				Return RefLeaf.Make("closref", NumToStr(id))
			Else
				Return RefLeaf.Make("varref", NumToStr(v.uid))
			EndIf
		Else	'free, i.e. _ENV.n
			v = getvar(_ft, "_ENV")
			Local id:Int = f.AddClos(v), env:Node = RefLeaf.Make("closref", NumToStr(id)), name:Node = Leaf.Make("fname", n.val)
			Return Rose.Make("getfield", [env, name], Null)
		EndIf
	End Method
	Method fcall:Node(n:Rose) { mcall pfcall pmcall tfcall tmcall va_fcall va_mcall }	'count arguments to function calls
		Local ac:Int
		Select n.key
			Case "fcall", "pfcall", "tfcall", "va_fcall"
				ac = Rose(n.arg[1]).arg.Length
			Default	'*mcall
				ac = Rose(Rose(n.arg[1]).arg[1]).arg.Length
		End Select
		Return Rose.Make(n.key, n.arg + [Leaf.Make("acount", ac)], Null)
	End Method

	Method Label:Node(n:Leaf) { GotoS }	'jump or dest
		Local f:FunDef = FunDef(_ft.First()), val:Int
		If n.key = "Label" Then val = f.AddLabel(n, Self) Else val = f.AddGoto(n)
		Return RefLeaf.Make(n.key, NumToStr(val))
	End Method

	Method ReturnS2:Node(n:Rose)	'detect and mark tail calls (on the way up to give the call a chance to process)
		If n.arg.Length = 1
			Local k:String = n.arg[0].key, c:Rose = Rose(n.arg[0])
			If k = "fcall" Or k = "mcall"
				Return Rose.Make("t" + k, c.arg, c.id)
			ElseIf k = "dotdotdot"
				Return Leaf.Make("return_va", "...")
			EndIf
		EndIf
		Return n
	End Method
End Type

Type FunDef
	Field n:Node, id:Int, env:FunDef
	Field vars:VarDef[], clos:VarDef[], pcount:Int, stksz:Int, vmax:Int, vcount:Int, hasVarargs:Int
	Field _ev:TList, _vt:TList, _ct:TList, _lt:TList, _gt:TList

	Function Make:FunDef(blk:Node, in:SemanticFold)
		Local f:FunDef = New FunDef
		f.env = FunDef(in._ft.First())
		f._vt = CreateList() ; f._ev = CreateList() ; f._ct = CreateList() ; f._lt = CreateList() ; f._gt = CreateList()
		in.funs.AddLast f ; in._ft.AddFirst f
		f.id = in.fcount ; in.fcount :+ 1
		Return f
	End Function
	Function Toplevel:FunDef(root:Node, in:SemanticFold)	'create a chunk toplevel as a function whose sole parameter is _ENV
		Local f:FunDef = FunDef.Make(root, in), env:VarDef = VarDef.Make("_ENV", Null, 0, 0, -1)
		f._ct.AddLast env ; Return f
	End Function
	Method AddVar:VarDef(n:Leaf, in:SemanticFold)
		Local blk:Node = Node(_ev.First()), v:VarDef
		For v = EachIn _vt
			If v.blk <> blk Then Exit	'the error here doesn't actually apply in standard Lua
		'	If v.name = n.val Then Throw CompileError.Make(TToken(in.tbl.ValueForKey(n)), "duplicate local variable '" + n.val + "'")
		Next
		v = VarDef.Make(n.val, blk, stksz, vcount, id) ; vcount :+ 1
		_vt.AddFirst v ; stksz :+ 1 ; Return v
	End Method
	Method AddClos:Int(v:VarDef)
		Local c:Int
		For Local e:VarDef = EachIn _ct
			If v = e Then Return c Else c :+ 1
		Next
		_ct.AddLast v ; Return c
	End Method
	Method AddLabel:Int(n:Leaf, in:SemanticFold)
		Local l:LabelDef = New LabelDef ; l.name = n.val ; l.depth = _ev.Count() ; l.varsvisible = stksz
		For Local ln:LabelDef = EachIn _lt
			If ln.depth = l.depth And ln.name = l.name Then Throw BlueCompileError.Make(TToken(in.tbl.ValueForKey(n)), "duplicate label '::" + n.val + "::'")
		Next
		_lt.AddLast l ; Return _lt.Count() - 1
	End Method
	Method AddGoto:Int(n:Leaf)
		Local g:GotoDef = New GotoDef ; g.name = n.val ; g.depth = _ev.Count() ; g.varsvisible = stksz ; g._n = n
		_gt.AddLast g ; Return _gt.Count() - 1
	End Method
	Method ResolveGotos(in:SemanticFold)
		Function cmp:Int(l:Object, r:Object)
			If LabelDef(l).depth > LabelDef(r).depth Return 1 ElseIf LabelDef(l).depth < LabelDef(r).depth Return -1 Else Return 0
		End Function
		_lt.Sort True, cmp
		For Local g:GotoDef = EachIn _gt
			Local l:LabelDef = Null
			For Local _l:LabelDef = EachIn _lt
				If g.depth < _l.depth Then Exit
				If g.name = _l.name Then l = _l ; Exit
			Next
			If l
				g.tgt = l ; g._n = Null
			Else
				Throw BlueCompileError.Make(TToken(in.tbl.ValueForKey(g._n)), "label '::" + g._n.val + "::' not visible in scope")
			EndIf
		Next
	End Method
	Method CloseBlock(n:Node, in:SemanticFold)
		vmax = Max(vmax, stksz)
		Local c:Int = 0, oldLen:Int = vars.Length
		For Local v:VarDef = EachIn _vt
			If v.blk <> n Then Exit
			c :+ 1 ; v.blk = Null	'noretain
		Next
		vars = New VarDef[c] + vars
		For Local i:Int = 0 Until c
			vars[i] = VarDef(_vt.RemoveFirst())	'pop contained vars
		Next
		stksz :- c
	End Method
	Method Close(_n:Node, in:SemanticFold)
		n = _n ; clos = VarDef[](_ct.ToArray()) ; ResolveGotos in
		Local v2:VarDef[] = New VarDef[vars.Length]		'vars aren't in the right order due to params, sort by uid
		For Local vi:Int = 0 Until v2.Length
			Local v:VarDef = vars[vi] ; v2[v.uid] = v
		Next
		vars = v2
		_vt = Null ; _ev = Null ; _ct = Null '; _lt = Null ; _gt = Null
	End Method
End Type

Type VarDef
	Field name:String, blk:Node, isClosure:Int, stkpos:Int, uid:Int, fid:Int
	Function Make:VarDef(n:String, b:Node, stkpos:Int, uid:Int, fid:Int)
		Local v:VarDef = New VarDef ; v.name = n ; v.stkpos = stkpos ; v.uid = uid ; v.fid = fid ; v.blk = b ; Return v
	End Function
End Type

Type LabelDef
	Field name:String, depth:Int, varsvisible:Int, insl:TLink, insc:Int = -1
End Type

Type GotoDef
	Field name:String, depth:Int, varsvisible:Int, _n:Leaf, tgt:LabelDef', op:Object
End Type

Type RefLeaf Extends Leaf
	Function Make:Leaf(key:String, val:String)
		Local l:Leaf = New RefLeaf
		l.key = key ; l.val = val
		Return l
	End Function
	Method ToString:String()
		Return "{ '#" + StrToNum(val) + "' : " + key + " }"
	End Method
End Type
Function StrToNum:Int(s:String)
	Return s[0] | (s[1] Shl 8) | (s[2] Shl 16) | (s[3] Shl 24)
End Function
Function NumToStr:String(n:Int)
	Return Chr(n & $FF) + Chr((n & $FF00) Shr 8) + Chr((n & $FF0000) Shr 16) + Chr((n & $FF000000) Shr 24)
End Function


Removed more '_currentworld' usage in b3dloader.

Strict

Rem
	bbdoc: Blitz3D model loader for MaxB3D
End Rem
Module MaxB3D.B3DLoader
ModuleInfo "Author: Kevin Primm"
ModuleInfo "License: MIT"

Import MaxB3D.Core
Import MaxB3D.BoneAnimator
Import Prime.libB3D

Type TMeshLoaderB3D Extends TMeshLoader
	Field _mesh:TMesh

	Method Run(config:TWorldConfig,mesh:TMesh,stream:TStream,url:Object)
		Local model:TBB3DChunk = TBB3DChunk(url)
		If model=Null model = TBB3DChunk.Load(stream)
		If model=Null Return False

		Local olddir$=CurrentDir()
		If String(url) ChangeDir(ExtractDir(String(url)))

		Local texture:TTexture[model.texs.length]
		Local brush:TBrush[model.brus.length]

		For Local i=0 To texture.length-1
			Local chunk:TTEXSChunk=model.texs[i]
			texture[i]=_currentworld.AddTexture(chunk.file,chunk.flags)
			If texture[i]
				texture[i].SetBlend chunk.blend
				texture[i].SetPosition chunk.x_pos,chunk.y_pos
				texture[i].SetScale chunk.x_scale,chunk.y_scale
				texture[i].SetRotation chunk.rotation*(180.0/Pi)
				If chunk.flags&65536 texture[i].SetCoords 1
			EndIf
		Next

		For Local i=0 To brush.length-1
			Local chunk:TBRUSChunk=model.brus[i]
			brush[i]=_currentworld.AddBrush()
			brush[i].SetName chunk.name
			brush[i].SetColor chunk.red*255,chunk.green*255,chunk.blue*255;brush[i].SetAlpha chunk.alpha
			brush[i].SetShine chunk.shininess
			brush[i].SetBlend chunk.blend;brush[i].SetFX chunk.fx
			For Local j=0 To chunk.n_texs-1
				If chunk.texture_id[j]>-1 brush[i].SetTexture texture[chunk.texture_id[j]],j
			Next
		Next
		ChangeDir olddir

		' model.dump StandardIOStream

		If model.node
			ParseNode config,model.node,mesh,brush,mesh
			_mesh = Null
			Return True
		EndIf
		Return False
	End Method

	Method ParseNode(config:TWorldConfig,node:TNODEChunk,parent:TEntity,brush:TBrush[],entity:TEntity=Null)
		Local meshchunk:TMESHChunk=TMESHChunk(node.kind),bonechunk:TBONEChunk=TBONEChunk(node.kind)

		Select node.kind
		Case Null
			entity=_currentworld.AddPivot(parent)
		Case meshchunk
			If entity=Null entity=New TMesh.Init(config, parent)
			Local mesh:TMesh=TMesh(entity)
			mesh._animator=New TBoneAnimator
			_mesh=mesh

			If meshchunk.brush_id>-1 entity.SetBrush brush[meshchunk.brush_id]

			Local vrts:TVRTSChunk=meshchunk.vrts
			Local vertsurface:TSurface=New TSurface
			vertsurface.Resize(vrts.xyz.length/3,0)

			For Local i=0 To vertsurface.CountVertices()-1
				vertsurface.SetCoords i,vrts.xyz[i*3+0],vrts.xyz[i*3+1],vrts.xyz[i*3+2]
				If vrts.nxyz vertsurface.SetNormal i,vrts.nxyz[i*3+0],vrts.nxyz[i*3+1],vrts.nxyz[i*3+2]
				If vrts.rgba vertsurface.SetColor i,vrts.rgba[i*4+0]*255,vrts.rgba[i*4+1]*255,vrts.rgba[i*4+2]*255,vrts.rgba[i*4+3]
				If vrts.SetSize()>1
					For Local j=0 To vrts.SetCount()-1
						vertsurface.SetTexCoords i,1-vrts.tex_coords[i][j,0],vrts.tex_coords[i][j,1],j
					Next
				EndIf
			Next

			For Local i=0 To meshchunk.tris.length-1
				Local tri:TTRISChunk=meshchunk.tris[i]
				Local surface:TSurface=vertsurface.Copy()
				surface.Resize(-1,tri.vertex_id.length/3)
				If tri.brush_id>-1 surface.SetBrush brush[tri.brush_id]
				For Local t=0 To surface.CountTriangles()-1
					surface.SetTriangle t,tri.vertex_id[t*3+0],tri.vertex_id[t*3+1],tri.vertex_id[t*3+2]
				Next
				If vrts.nxyz=Null surface.UpdateNormals
				mesh.AppendSurface surface
			Next
		Case bonechunk
			entity=New TBone.Init(config, parent)
			Local bone:TBone=TBone(entity)

			For Local surface:TSurface=EachIn _mesh._surfaces
				bone.AddSurface surface
				For Local i=0 To bonechunk.vertex_id.length-1
					bone.AddVertex surface,bonechunk.vertex_id[i],bonechunk.weight[i]
				Next
			Next

			Local ident_matrix:TMatrix=TMatrix.Identity()
			For Local chunk:TKEYSChunk=EachIn node.keys
				Local keys:TAnimKey[chunk.frame.length]
				Local pos#[]=chunk.position,rot#[]=chunk.rotation,scl#[]=chunk.scale
				For Local i=0 To chunk.frame.length-1
					Local bonekey:TBoneKey=New TBoneKey

					If pos.length>0 bonekey._px=pos[i*3+0];bonekey._py=pos[i*3+1];bonekey._pz=pos[i*3+2]
					If rot.length>0 bonekey._rw=rot[i*4+0];bonekey._rx=rot[i*4+1];bonekey._ry=rot[i*4+2];bonekey._rz=rot[i*4+3]
					If scl.length>0 bonekey._sx=scl[i*3+0];bonekey._sy=scl[i*3+1];bonekey._sz=scl[i*3+2]

					Local key:TAnimKey=New TAnimKey
					key._frame=chunk.frame[i]
					key._object=bonekey
					keys[i]=key
				Next
				TBoneAnimator(_mesh._animator).AddBone bone,keys
			Next
		Default
			entity=New TPivot.Init(config, parent)
		End Select

		entity.SetName node.name
		entity.SetPosition node.position[0],node.position[1],node.position[2]
		Local pitch#,yaw#,roll#
		TQuaternion.Euler node.rotation[0],node.rotation[1],node.rotation[2],node.rotation[3],pitch,yaw,roll
		entity.SetRotation pitch,yaw,roll
		entity.SetScale node.scale[0],node.scale[1],node.scale[2]

		For Local child:TNODEChunk=EachIn node.node
			ParseNode config,child,entity,brush
		Next
		If TMesh(entity) If TBoneAnimator(TMesh(entity)._animator)._root=Null TMesh(entity)._animator=Null
		If TBone(entity) TBone(entity)._start_matrix=entity.GetMatrix()
	End Method

	Method Info$()
		Return "Blitz3D|b3d"
	End Method
	Method ModuleName$()
		Return "b3dloader"
	End Method
End Type
New TMeshLoaderB3D

Strict

Rem
	bbdoc: Blitz3D model loader for MaxB3D
End Rem
Module MaxB3D.B3DLoader
ModuleInfo "Author: Kevin Primm"
ModuleInfo "License: MIT"

Import MaxB3D.Core
Import MaxB3D.BoneAnimator
Import Prime.libB3D

Type TMeshLoaderB3D Extends TMeshLoader
	Field _mesh:TMesh

	Method Run(config:TWorldConfig,mesh:TMesh,stream:TStream,url:Object)
		Local model:TBB3DChunk = TBB3DChunk(url)
		If model=Null model = TBB3DChunk.Load(stream)
		If model=Null Return False

		Local olddir$=CurrentDir()
		If String(url) ChangeDir(ExtractDir(String(url)))

		Local texture:TTexture[model.texs.length]
		Local brush:TBrush[model.brus.length]

		For Local i=0 To texture.length-1
			Local chunk:TTEXSChunk=model.texs[i]
			texture[i]=_currentworld.AddTexture(chunk.file,chunk.flags)
			If texture[i]
				texture[i].SetBlend chunk.blend
				texture[i].SetPosition chunk.x_pos,chunk.y_pos
				texture[i].SetScale chunk.x_scale,chunk.y_scale
				texture[i].SetRotation chunk.rotation*(180.0/Pi)
				If chunk.flags&65536 texture[i].SetCoords 1
			EndIf
		Next

		For Local i=0 To brush.length-1
			Local chunk:TBRUSChunk=model.brus[i]
			brush[i]=_currentworld.AddBrush()
			brush[i].SetName chunk.name
			brush[i].SetColor chunk.red*255,chunk.green*255,chunk.blue*255;brush[i].SetAlpha chunk.alpha
			brush[i].SetShine chunk.shininess
			brush[i].SetBlend chunk.blend;brush[i].SetFX chunk.fx
			For Local j=0 To chunk.n_texs-1
				If chunk.texture_id[j]>-1 brush[i].SetTexture texture[chunk.texture_id[j]],j
			Next
		Next
		ChangeDir olddir

		' model.dump StandardIOStream

		If model.node
			ParseNode config,model.node,mesh,brush,mesh
			_mesh = Null
			Return True
		EndIf
		Return False
	End Method

	Method ParseNode(config:TWorldConfig,node:TNODEChunk,parent:TEntity,brush:TBrush[],entity:TEntity=Null)
		Local meshchunk:TMESHChunk=TMESHChunk(node.kind),bonechunk:TBONEChunk=TBONEChunk(node.kind)

		Select node.kind
		Case Null
			entity=_currentworld.AddPivot(parent)
		Case meshchunk
			If entity=Null entity=New TMesh.Init(config, parent)
			Local mesh:TMesh=TMesh(entity)
			mesh._animator=New TBoneAnimator
			_mesh=mesh

			If meshchunk.brush_id>-1 entity.SetBrush brush[meshchunk.brush_id]

			Local vrts:TVRTSChunk=meshchunk.vrts
			Local vertsurface:TSurface=New TSurface
			vertsurface.Resize(vrts.xyz.length/3,0)

			For Local i=0 To vertsurface.CountVertices()-1
				vertsurface.SetCoords i,vrts.xyz[i*3+0],vrts.xyz[i*3+1],vrts.xyz[i*3+2]
				If vrts.nxyz vertsurface.SetNormal i,vrts.nxyz[i*3+0],vrts.nxyz[i*3+1],vrts.nxyz[i*3+2]
				If vrts.rgba vertsurface.SetColor i,vrts.rgba[i*4+0]*255,vrts.rgba[i*4+1]*255,vrts.rgba[i*4+2]*255,vrts.rgba[i*4+3]
				If vrts.SetSize()>1
					For Local j=0 To vrts.SetCount()-1
						vertsurface.SetTexCoords i,1-vrts.tex_coords[i][j,0],vrts.tex_coords[i][j,1],j
					Next
				EndIf
			Next

			For Local i=0 To meshchunk.tris.length-1
				Local tri:TTRISChunk=meshchunk.tris[i]
				Local surface:TSurface=vertsurface.Copy()
				surface.Resize(-1,tri.vertex_id.length/3)
				If tri.brush_id>-1 surface.SetBrush brush[tri.brush_id]
				For Local t=0 To surface.CountTriangles()-1
					surface.SetTriangle t,tri.vertex_id[t*3+0],tri.vertex_id[t*3+1],tri.vertex_id[t*3+2]
				Next
				If vrts.nxyz=Null surface.UpdateNormals
				mesh.AppendSurface surface
			Next
		Case bonechunk
			entity=_currentworld.AddBone(parent)
			Local bone:TBone=TBone(entity)

			For Local surface:TSurface=EachIn _mesh._surfaces
				bone.AddSurface surface
				For Local i=0 To bonechunk.vertex_id.length-1
					bone.AddVertex surface,bonechunk.vertex_id[i],bonechunk.weight[i]
				Next
			Next

			Local ident_matrix:TMatrix=TMatrix.Identity()
			For Local chunk:TKEYSChunk=EachIn node.keys
				Local keys:TAnimKey[chunk.frame.length]
				Local pos#[]=chunk.position,rot#[]=chunk.rotation,scl#[]=chunk.scale
				For Local i=0 To chunk.frame.length-1
					Local bonekey:TBoneKey=New TBoneKey

					If pos.length>0 bonekey._px=pos[i*3+0];bonekey._py=pos[i*3+1];bonekey._pz=pos[i*3+2]
					If rot.length>0 bonekey._rw=rot[i*4+0];bonekey._rx=rot[i*4+1];bonekey._ry=rot[i*4+2];bonekey._rz=rot[i*4+3]
					If scl.length>0 bonekey._sx=scl[i*3+0];bonekey._sy=scl[i*3+1];bonekey._sz=scl[i*3+2]

					Local key:TAnimKey=New TAnimKey
					key._frame=chunk.frame[i]
					key._object=bonekey
					keys[i]=key
				Next
				TBoneAnimator(_mesh._animator).AddBone bone,keys
			Next
		Default
			entity=_currentworld.AddPivot(parent)
		End Select

		entity.SetName node.name
		entity.SetPosition node.position[0],node.position[1],node.position[2]
		Local pitch#,yaw#,roll#
		TQuaternion.Euler node.rotation[0],node.rotation[1],node.rotation[2],node.rotation[3],pitch,yaw,roll
		entity.SetRotation pitch,yaw,roll
		entity.SetScale node.scale[0],node.scale[1],node.scale[2]

		For Local child:TNODEChunk=EachIn node.node
			ParseNode config,child,entity,brush
		Next
		If TMesh(entity) If TBoneAnimator(TMesh(entity)._animator)._root=Null TMesh(entity)._animator=Null
		If TBone(entity) TBone(entity)._start_matrix=entity.GetMatrix()
	End Method

	Method Info$()
		Return "Blitz3D|b3d"
	End Method
	Method ModuleName$()
		Return "b3dloader"
	End Method
End Type
New TMeshLoaderB3D


Added stub SeekMusic to bmax

Added stub SeekMusic to bmax

' ***** Start diddy.bmax.bmx ******
Global diddy_mouseWheel:Float = 0.0

Type diddy
	Function systemMillisecs:Float()
		Return Millisecs()
	EndFunction

	Function setGraphics(w:int, h:int, fullScreen:Int = False)
		local d% = 0
		if fullScreen then d = 32
		Graphics w, h, d, 60
	EndFunction

	Function setMouse(x:Int, y:Int)
		MoveMouse(x, y)
	EndFunction

	Function showKeyboard()
	EndFunction

	Function launchBrowser(address:String, windowName:String)
	EndFunction

	Function launchEmail(email:String, subject:String, text:String)
	EndFunction

	Function realMod:Float( value:Float, amount:Float )
		Return value Mod amount
	EndFunction

	Function startVibrate(millisecs:int)
	EndFunction

	Function stopVibrate()
	EndFunction

	Function getDayOfMonth:Int()
		Local date$ = CurrentDate()
		Return Int(date[..2])
	EndFunction

	Function getDayOfWeek:Int()
		Return 0
	EndFunction

	Function getMonth:Int()
		Local date$ = CurrentDate()
		Return (Instr("JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC", date$[3..6].ToUpper(), 1) / 3) + 1
	EndFunction

	Function getYear:Int()
		Local date$ = CurrentDate()
		Return Int(date[date.length - 4..]);
	EndFunction

	Function getHours:Int()
		Local time$ = CurrentTime()
		Return Int(time[..2])
	EndFunction

	Function getMinutes:Int()
		Local time$ = CurrentTime()
		Return Int(time[3..5])
	EndFunction

	Function getSeconds:Int()
		Local time$ = CurrentTime()
		Return Int(time[6..8])
	EndFunction

	Function getMilliSeconds:Int()
		Return Millisecs()
	EndFunction

	Function startGps()
	EndFunction

	Function getLatitiude:String()
		Return ""
	EndFunction

	Function getLongitude:String()
		Return ""
	EndFunction

	Function showAlertDialog(title:String, message:String)
	EndFunction

	Function getInputString:String()
		Return "";
	EndFunction

	Function getPixel:Int( x:int, y:int )
'		unsigned char pix[4];
'		glReadPixels(x, app->graphics->height-y ,1 ,1 ,GL_RGBA ,GL_UNSIGNED_BYTE ,pix);
'		return (pix[3]<<24) | (pix[0]<<16) | (pix[1]<<8) |  pix[2];
		Return 0
	EndFunction

	Function mouseZInit()
	EndFunction

	Function mouseZ:Float()
		Local ret:Float = BlitzMaxMouseZ() - diddy_mouseWheel
		diddy_mouseWheel = BlitzMaxMouseZ()
		Return ret
	EndFunction

	Function SeekMusic:Int(timeMillis:Int)
		Return 0
	EndFunction
EndType

Function BlitzMaxMouseZ:Float()
	Return MouseZ()
EndFunction

' ***** End diddy.bmax.bmx ******

' ***** Start diddy.bmax.bmx ******
Global diddy_mouseWheel:Float = 0.0

Type diddy
	Function systemMillisecs:Float()
		Return Millisecs()
	EndFunction

	Function setGraphics(w:int, h:int, fullScreen:Int = False)
		local d% = 0
		if fullScreen then d = 32
		Graphics w, h, d, 60
	EndFunction

	Function setMouse(x:Int, y:Int)
		MoveMouse(x, y)
	EndFunction

	Function showKeyboard()
	EndFunction

	Function launchBrowser(address:String, windowName:String)
	EndFunction

	Function launchEmail(email:String, subject:String, text:String)
	EndFunction

	Function realMod:Float( value:Float, amount:Float )
		Return value Mod amount
	EndFunction

	Function startVibrate(millisecs:int)
	EndFunction

	Function stopVibrate()
	EndFunction

	Function getDayOfMonth:Int()
		Local date$ = CurrentDate()
		Return Int(date[..2])
	EndFunction

	Function getDayOfWeek:Int()
		Return 0
	EndFunction

	Function getMonth:Int()
		Local date$ = CurrentDate()
		Return (Instr("JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC", date$[3..6].ToUpper(), 1) / 3) + 1
	EndFunction

	Function getYear:Int()
		Local date$ = CurrentDate()
		Return Int(date[date.length - 4..]);
	EndFunction

	Function getHours:Int()
		Local time$ = CurrentTime()
		Return Int(time[..2])
	EndFunction

	Function getMinutes:Int()
		Local time$ = CurrentTime()
		Return Int(time[3..5])
	EndFunction

	Function getSeconds:Int()
		Local time$ = CurrentTime()
		Return Int(time[6..8])
	EndFunction

	Function getMilliSeconds:Int()
		Return Millisecs()
	EndFunction

	Function startGps()
	EndFunction

	Function getLatitiude:String()
		Return ""
	EndFunction

	Function getLongitude:String()
		Return ""
	EndFunction

	Function showAlertDialog(title:String, message:String)
	EndFunction

	Function getInputString:String()
		Return "";
	EndFunction

	Function getPixel:Int( x:int, y:int )
'		unsigned char pix[4];
'		glReadPixels(x, app->graphics->height-y ,1 ,1 ,GL_RGBA ,GL_UNSIGNED_BYTE ,pix);
'		return (pix[3]<<24) | (pix[0]<<16) | (pix[1]<<8) |  pix[2];
		Return 0
	EndFunction

	Function mouseZInit()
	EndFunction

	Function mouseZ:Float()
		Local ret:Float = BlitzMaxMouseZ() - diddy_mouseWheel
		diddy_mouseWheel = BlitzMaxMouseZ()
		Return ret
	EndFunction
EndType

Function BlitzMaxMouseZ:Float()
	Return MouseZ()
EndFunction

' ***** End diddy.bmax.bmx ******


[json] (v0.2) 	dJNullVariable.ValueAsString now returns Null (instead of "null") 	Documentation correction

Rem
	Copyright (c) 2009 Tim Howard

	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.
End Rem

SuperStrict

Rem
bbdoc: JSON handler for cower.jonk
End Rem
Module duct.json

ModuleInfo "Version: 0.2"
ModuleInfo "Copyright: Tim Howard"
ModuleInfo "License: MIT"

ModuleInfo "History: Version 0.2"
ModuleInfo "History: dJNullVariable.ValueAsString now returns Null (instead of ~qnull~q); documentation correction"
ModuleInfo "History: Version 0.1"
ModuleInfo "History: Initial version."

Import duct.variables
Import cower.jonk

Rem
	bbdoc: duct JSON object.
End Rem
Type dJObject Extends TIdentifier

	Rem
		bbdoc: Create a new dJObject.
		returns: The new dJObject (itself).
	End Rem
	Method Create:dJObject()
		Super.Create()
		Return Self
	End Method

	Rem
		bbdoc: Get the object's parent.
		returns: The object's parent.
	End Rem
	Method GetParent:dJObject()
		Return dJObject(m_parent)
	End Method

End Type

Rem
	bbdoc: duct JSON array.
End Rem
Type dJArray Extends dJObject

	Rem
		bbdoc: Create a new dJArray.
		returns: The new dJArray (itself).
	End Rem
	Method Create:dJArray()
		Super.Create()
		Return Self
	End Method

End Type

Const TV_NULL:Int = 100

Rem
	bbdoc: duct JSON null variable.
End Rem
Type dJNullVariable Extends TVariable

	Rem
		bbdoc: Create a New dJNullVariable.
		returns: The New dJNullVariable (itself).
	End Rem
	Method Create:dJNullVariable(name:String = Null)
		SetName(name)
		Return Self
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the value of the variable To the given String (ambiguous).
		returns: Nothing.
		about: This does nothing for dJNullVariable.
	End Rem
	Method SetAmbiguous(value:String)
	End Method

	Rem
		bbdoc: Convert the variable to a String.
		returns: A string representation of the variable.
		about: This function is for script output, for in-code use see #ValueAsString.
	End Rem
	Method ConvToString:String()
		Return "null"
	End Method

	Rem
		bbdoc: Get the variable as a String.
		returns: The variable's value converted to a String.
	End Rem
	Method ValueAsString:String()
		Return Null
	End Method

'#end region (Field accessors)

'#region Data handling

	Rem
		bbdoc: Create a copy of the variable
		returns: A clone of the variable.
	End Rem
	Method Copy:dJNullVariable()
		Return New dJNullVariable.Create(m_name)
	End Method

	Rem
		bbdoc: Serialize the variable to a stream.
		returns: Nothing.
		about: @tv tells the method whether it should serialize the TV type for the variable, or to not.
	End Rem
	Method Serialize(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.WriteByte(TV_INTEGER)
		End If
		If name = True
			WriteLString(stream, m_name)
		End If
	End Method

	Rem
		bbdoc: Deserialize the variable from a stream.
		returns: The deserialized variable.
		about: @tv tells the method whether it should deserialize the TV type for the variable, or to not.
	End Rem
	Method DeSerialize:dJNullVariable(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.ReadByte()
		End If
		If name = True
			m_name = ReadLString(stream)
		End If
		Return Self
	End Method

'#end region (Data handling)

	Rem
		bbdoc: Get the type of this variable.
		returns: The type of this variable ("null").
	End Rem
	Function ReportType:String()
		Return "null"
	End Function

	Rem
		bbdoc: Get the TV type of this variable.
		returns: The TV_* type of this variable (TV_NULL).
	End Rem
	Function GetTVType:Int()
		Return TV_NULL
	End Function

End Type

Rem
	bbdoc: duct event handler for JONK.
End Rem
Type dJEventHandler Extends JEventHandler

	Field m_objname:String
	Field m_root:dJObject
	Field m_object:dJObject

	Field tmpvar:TVariable

	Method BeginParsing()
		m_root = New dJObject.Create()
		m_object = m_root
	End Method

	Method EndParsing()
		Assert m_object = m_root, "(dJEventHandler.EndParsing) m_object != m_root!"
		m_objname = Null
		m_object = Null
	End Method

	Method ObjectBegin()
		tmpvar = New dJObject.Create()
		SetAndClearName(tmpvar)
		m_object.AddValue(tmpvar)
		m_object = dJObject(tmpvar)
	End Method

	Method ObjectKey(name:String)
		m_objname = name
	End Method

	Method ObjectEnd()
		m_object = dJObject(m_object.GetParent())
	End Method

	Method ArrayBegin()
		tmpvar = New dJArray.Create()
		SetAndClearName(tmpvar)
		m_object.AddValue(tmpvar)
		m_object = dJObject(tmpvar)
	End Method

	Method ArrayEnd()
		m_object = dJObject(m_object.GetParent())
	End Method

	Method NumberValue(number:String, isdecimal:Int)
		If isdecimal = True
			tmpvar = New TFloatVariable.Create(Null, Float(number))
		Else
			tmpvar = New TIntVariable.Create(Null, Int(number))
		End If
		SetAndClearName(tmpvar)
		m_object.AddValue(tmpvar)
	End Method

	Method StringValue(value:String)
		tmpvar = New TStringVariable.Create(Null, value)
		SetAndClearName(tmpvar)
		m_object.AddValue(tmpvar)
	End Method

	Method BooleanValue(value:Int)
		tmpvar = New TBoolVariable.Create(Null, value)
		SetAndClearName(tmpvar)
		m_object.AddValue(tmpvar)
	End Method

	Method NullValue()
		tmpvar = New dJNullVariable.Create(Null)
		SetAndClearName(tmpvar)
		m_object.AddValue(tmpvar)
	End Method

	Method Error:Int(err:JParserException)
		Return False
	End Method

	Method SetAndClearName(variable:TVariable)
		variable.SetName(m_objname)
		m_objname = Null
	End Method

End Type

Rem
	bbdoc: duct JSON reader.
End Rem
Type dJReader

	Field m_evthandler:dJEventHandler
	Field m_parser:JParser
	Field m_root:dJObject

	Method New()
		m_evthandler = New dJEventHandler
	End Method

	Rem
		bbdoc: Initiate the reader with the given url/stream.
		returns: Itself, or Null if the stream could not be opened.
	End Rem
	Method InitWithStream:dJReader(url:Object, encoding:Int = JSONEncodingUTF8, bufferLength:Int = JParser.JPARSERBUFFER_INITIAL_SIZE)
		Try
			m_parser = New JParser.InitWithStream(url, m_evthandler, encoding, bufferLength)
			Return Self
		Catch e:JException
			Return Null
		End Try
	End Method

	Rem
		bbdoc: Initiate the reader with the given string.
		returns: Itself.
	End Rem
	Method InitWithString:dJReader(str:String)
		m_parser = New JParser.InitWithString(str, m_evthandler)
		Return Self
	End Method

	Rem
		bbdoc: Parse the reader's data.
		returns: The root object for the json data.
		about: A #JParserException will be thrown if there is an error during parsing.
	End Rem
	Method Parse:dJObject()
		m_parser.Parse()
		Return m_evthandler.m_root
	End Method

End Type

Rem
	Copyright (c) 2009 Tim Howard

	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.
End Rem

SuperStrict

Rem
bbdoc: JSON handler for cower.jonk
End Rem
Module duct.json

ModuleInfo "Version: 0.1"
ModuleInfo "Copyright: Tim Howard"
ModuleInfo "License: MIT"

ModuleInfo "History: Version 0.1"
ModuleInfo "History: Initial version."

Import duct.variables
Import cower.jonk

Rem
	bbdoc: duct JSON object.
End Rem
Type dJObject Extends TIdentifier

	Rem
		bbdoc: Create a new dJObject.
		returns: The new dJObject (itself).
	End Rem
	Method Create:dJObject()
		Super.Create()
		Return Self
	End Method

	Rem
		bbdoc: Get the object's parent.
		returns: The object's parent.
	End Rem
	Method GetParent:dJObject()
		Return dJObject(m_parent)
	End Method

End Type

Rem
	bbdoc: duct JSON array.
End Rem
Type dJArray Extends dJObject

	Rem
		bbdoc: Create a new dJArray.
		returns: The new dJArray (itself).
	End Rem
	Method Create:dJArray()
		Super.Create()
		Return Self
	End Method

End Type

Const TV_NULL:Int = 100

Rem
	bbdoc: duct JSON null variable.
End Rem
Type dJNullVariable Extends TVariable

	Rem
		bbdoc: Create a New dJNullVariable.
		returns: The New dJNullVariable (itself).
	End Rem
	Method Create:dJNullVariable(name:String = Null)
		SetName(name)
		Return Self
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the value of the variable To the given String (ambiguous).
		returns: Nothing.
		about: This does nothing for dJNullVariable.
	End Rem
	Method SetAmbiguous(value:String)
	End Method

	Rem
		bbdoc: Convert the variable to a String.
		returns: A string representation of the variable.
		about: This function is for script output, for in-code use see #ValueAsString.
	End Rem
	Method ConvToString:String()
		Return "null"
	End Method

	Rem
		bbdoc: Get the IntVariable as a String.
		returns: The variable's value converted to a String.
	End Rem
	Method ValueAsString:String()
		Return "null"
	End Method

'#end region (Field accessors)

'#region Data handling

	Rem
		bbdoc: Create a copy of the variable
		returns: A clone of the variable.
	End Rem
	Method Copy:dJNullVariable()
		Return New dJNullVariable.Create(m_name)
	End Method

	Rem
		bbdoc: Serialize the variable to a stream.
		returns: Nothing.
		about: @tv tells the method whether it should serialize the TV type for the variable, or to not.
	End Rem
	Method Serialize(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.WriteByte(TV_INTEGER)
		End If
		If name = True
			WriteLString(stream, m_name)
		End If
	End Method

	Rem
		bbdoc: Deserialize the variable from a stream.
		returns: The deserialized variable.
		about: @tv tells the method whether it should deserialize the TV type for the variable, or to not.
	End Rem
	Method DeSerialize:dJNullVariable(stream:TStream, tv:Int = True, name:Int = False)
		If tv = True
			stream.ReadByte()
		End If
		If name = True
			m_name = ReadLString(stream)
		End If
		Return Self
	End Method

'#end region (Data handling)

	Rem
		bbdoc: Get the type of this variable.
		returns: The type of this variable ("null").
	End Rem
	Function ReportType:String()
		Return "null"
	End Function

	Rem
		bbdoc: Get the TV type of this variable.
		returns: The TV_* type of this variable (TV_NULL).
	End Rem
	Function GetTVType:Int()
		Return TV_NULL
	End Function

End Type

Rem
	bbdoc: duct event handler for JONK.
End Rem
Type dJEventHandler Extends JEventHandler

	Field m_objname:String
	Field m_root:dJObject
	Field m_object:dJObject

	Field tmpvar:TVariable

	Method BeginParsing()
		m_root = New dJObject.Create()
		m_object = m_root
	End Method

	Method EndParsing()
		Assert m_object = m_root, "(dJEventHandler.EndParsing) m_object != m_root!"
		m_objname = Null
		m_object = Null
	End Method

	Method ObjectBegin()
		tmpvar = New dJObject.Create()
		SetAndClearName(tmpvar)
		m_object.AddValue(tmpvar)
		m_object = dJObject(tmpvar)
	End Method

	Method ObjectKey(name:String)
		m_objname = name
	End Method

	Method ObjectEnd()
		m_object = dJObject(m_object.GetParent())
	End Method

	Method ArrayBegin()
		tmpvar = New dJArray.Create()
		SetAndClearName(tmpvar)
		m_object.AddValue(tmpvar)
		m_object = dJObject(tmpvar)
	End Method

	Method ArrayEnd()
		m_object = dJObject(m_object.GetParent())
	End Method

	Method NumberValue(number:String, isdecimal:Int)
		If isdecimal = True
			tmpvar = New TFloatVariable.Create(Null, Float(number))
		Else
			tmpvar = New TIntVariable.Create(Null, Int(number))
		End If
		SetAndClearName(tmpvar)
		m_object.AddValue(tmpvar)
	End Method

	Method StringValue(value:String)
		tmpvar = New TStringVariable.Create(Null, value)
		SetAndClearName(tmpvar)
		m_object.AddValue(tmpvar)
	End Method

	Method BooleanValue(value:Int)
		tmpvar = New TBoolVariable.Create(Null, value)
		SetAndClearName(tmpvar)
		m_object.AddValue(tmpvar)
	End Method

	Method NullValue()
		tmpvar = New dJNullVariable.Create(Null)
		SetAndClearName(tmpvar)
		m_object.AddValue(tmpvar)
	End Method

	Method Error:Int(err:JParserException)
		Return False
	End Method

	Method SetAndClearName(variable:TVariable)
		variable.SetName(m_objname)
		m_objname = Null
	End Method

End Type

Rem
	bbdoc: duct JSON reader.
End Rem
Type dJReader

	Field m_evthandler:dJEventHandler
	Field m_parser:JParser
	Field m_root:dJObject

	Method New()
		m_evthandler = New dJEventHandler
	End Method

	Rem
		bbdoc: Initiate the reader with the given url/stream.
		returns: Itself, or Null if the stream could not be opened.
	End Rem
	Method InitWithStream:dJReader(url:Object, encoding:Int = JSONEncodingUTF8, bufferLength:Int = JParser.JPARSERBUFFER_INITIAL_SIZE)
		Try
			m_parser = New JParser.InitWithStream(url, m_evthandler, encoding, bufferLength)
			Return Self
		Catch e:JException
			Return Null
		End Try
	End Method

	Rem
		bbdoc: Initiate the reader with the given string.
		returns: Itself.
	End Rem
	Method InitWithString:dJReader(str:String)
		m_parser = New JParser.InitWithString(str, m_evthandler)
		Return Self
	End Method

	Rem
		bbdoc: Parse the reader's data.
		returns: The root object for the json data.
		about: A #JParserException will be thrown if there is an error during parsing.
	End Rem
	Method Parse:dJObject()
		m_parser.Parse()
		Return m_evthandler.m_root
	End Method

End Type


Added module access methods to mxSourcesHandler.

Rem
Copyright (c) 2010 Tim Howard

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.
End Rem

Rem
	bbdoc: Maximus sources handler.
End Rem
Type mxSourcesHandler Extends dObjectMap

	Rem
		bbdoc: Add the given scope to the handler.
		returns: True if the scope was added, or False if it was not (scope is Null).
	End Rem
	Method AddScope:Int(modscope:mxModuleScope)
		If modscope <> Null
			_Insert(modscope.GetName(), modscope)
			Return True
		End If
		Return False
	End Method

	Rem
		bbdoc: Check if the handler has a scope with the given name.
		returns: True if a scope with the given name was found, or False if there is no scope with the given name.
	End Rem
	Method HasScope:Int(scopename:String)
		Return _Contains(scopename)
	End Method

	Rem
		bbdoc: Check if the given module is in the sources.
		returns: True if the sources contains the given module, or False if it does not.
	End Rem
	Method HasModule:Int(modid:String)
		Local scope:mxModuleScope = GetScopeWithName(mxModUtils.GetScopeFromID(modid))
		If scope <> Null
			Return scope.HasModule(mxModUtils.GetNameFromID(modid))
		End If
		Return False
	End Method

	Rem
		bbdoc: Get the scope with the given name.
		returns: The scope with the given name, or Null if there is no scope with the given name.
	End Rem
	Method GetScopeWithName:mxModuleScope(scopename:String)
		Return mxModuleScope(_ValueByKey(scopename))
	End Method

	Rem
		bbdoc: Get the module with the given id.
		returns: The module with the given id, or Null if there is no module with the given id.
	End Rem
	Method GetModuleWithID:mxModule(modid:String)
		Local scope:mxModuleScope = GetScopeWithName(mxModUtils.GetScopeFromID(modid))
		If scope <> Null
			Return scope.GetModuleWithName(mxModUtils.GetNameFromID(modid))
		End If
		Return Null
	End Method

	Rem
		bbdoc: Get the module version with the given versioned-id.
		returns: The module version with the given versioned-id, or Null if there is no module/version with the given versioned-id.
	End Rem
	Method GetVersionWithVerID:mxModuleVersion(verid:String)
		Local modul:mxModule = GetModuleWithID(verid)
		If modul <> Null
			Return modul.GetVersionWithName(mxModUtils.GetVersionFromVerID(verid))
		End If
		Return Null
	End Method

	Rem
		bbdoc: Load the given file into the handler.
		returns: Itself, or Null if the given file either could not be opened or does not exist.
	End Rem
	Method FromFile:mxSourcesHandler(file:String)
		Local jreader:dJReader, root:dJObject
		jreader = New dJReader.InitWithStream(file)
		If jreader <> Null
			Try
				root = jreader.Parse()
			Catch e:JException
				ThrowError(_s("errors.load.sources.parse", [e.ToString()]))
			End Try
			FromJSON(root)
			Return Self
		End If
		Return Null
	End Method

	Rem
		bbdoc: Load the given dJObject into the handler.
		returns: Itself, or Null if @root is Null.
	End Rem
	Method FromJSON:mxSourcesHandler(root:dJObject)
		If root <> Null
			For Local obj:dJObject = EachIn root.GetValues()
				AddScope(New mxModuleScope.FromJSON(obj))
			Next
			Return Self
		End If
		Return Null
	End Method

End Type

Rem
Copyright (c) 2010 Tim Howard

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.
End Rem

Rem
	bbdoc: Maximus sources handler.
End Rem
Type mxSourcesHandler Extends dObjectMap

	Rem
		bbdoc: Add the given scope to the handler.
		returns: True if the scope was added, or False if it was not (scope is Null).
	End Rem
	Method AddScope:Int(modscope:mxModuleScope)
		If modscope <> Null
			_Insert(modscope.GetName(), modscope)
			Return True
		End If
		Return False
	End Method

	Rem
		bbdoc: Check if the handler has a scope with the given name.
		returns: True if a scope with the given name was found, or False if there is no scope with the given name.
	End Rem
	Method HasScope:Int(scopename:String)
		Return _Contains(scopename)
	End Method

	Rem
		bbdoc: Get the scope with the given name.
		returns: The scope with the given name, or Null if there is no scope with the given name.
	End Rem
	Method GetScopeWithName:mxModuleScope(scopename:String)
		Return mxModuleScope(_ValueByKey(scopename))
	End Method

	Rem
		bbdoc: Load the given file into the handler.
		returns: Itself, or Null if the given file either could not be opened or does not exist.
	End Rem
	Method FromFile:mxSourcesHandler(file:String)
		Local jreader:dJReader, root:dJObject
		jreader = New dJReader.InitWithStream(file)
		If jreader <> Null
			Try
				root = jreader.Parse()
			Catch e:JException
				ThrowError(_s("errors.load.sources.parse", [e.ToString()]))
			End Try
			FromJSON(root)
			Return Self
		End If
		Return Null
	End Method

	Rem
		bbdoc: Load the given dJObject into the handler.
		returns: Itself, or Null if @root is Null.
	End Rem
	Method FromJSON:mxSourcesHandler(root:dJObject)
		If root <> Null
			For Local obj:dJObject = EachIn root.GetValues()
				AddScope(New mxModuleScope.FromJSON(obj))
			Next
			Return Self
		End If
		Return Null
	End Method

End Type