Added brl.system import for Linux (X11 dependency issue in 1.39).

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

SuperStrict

Framework brl.blitz
Import brl.standardio
Import brl.maxutil
Import brl.ramstream

' We have to import this for BMax 1.39 on Linux because of a dependency issue in brl.system
' http://blitzbasic.com/Community/posts.php?topic=90547
?Linux
Import brl.glgraphics
?

Import gman.zipengine
Import htbaapub.rest

Import duct.variables
Import duct.objectmap
Import duct.json
Import duct.locale
Import duct.argparser

Incbin "locales/en.loc"

Include "src/logger.bmx"
Include "src/errors.bmx"
Include "src/config.bmx"
Include "src/dependencies.bmx"
Include "src/module.bmx"
Include "src/sources.bmx"
Include "src/utils.bmx"
Include "src/arghandler.bmx"
Include "src/impl/help.bmx"
Include "src/impl/version.bmx"
Include "src/impl/modpath.bmx"
Include "src/impl/install.bmx"
Include "src/impl/update.bmx"
Include "src/impl/list.bmx"

Global logger:mxLogger = New mxLogger
Global mainapp:mxApp
New mxApp.Create(AppArgs[1..]) ' Skip the first element because it is the program's location
mainapp.Run()

Rem
	bbdoc: Maximus app.
	about: This handles the basic flow of the program (initiation, parsing, command calling, exiting..)
End Rem
Type mxApp

	Const c_version:String = "0.01"
	Const c_configfile:String = "maximus.config"

	Field m_confighandler:mxConfigHandler
	Field m_defaultlocale:dLocale, m_locale:dLocale
	Field m_maxpath:String, m_modpath:String
	Field m_sourcesfile:String = "sources", m_sourcesurl:String = "http://maximus.htbaa.com/module/sources/json"
	Field m_useragent:String

	Field m_args:dIdentifier
	Field m_arghandler:mxArgumentHandler
	Field m_sourceshandler:mxSourcesHandler

	Rem
		bbdoc: Create a new mxApp.
		returns: Itself.
	End Rem
	Method Create:mxApp(args:String[])
		mainapp = Self
		SetArgs(args)
		OnInit()
		Return Self
	End Method

	Rem
		bbdoc: Called when the application is created.
		returns: Nothing.
	End Rem
	Method OnInit()
		Try
			m_maxpath = BlitzMaxPath()
		Catch e:Object
			ThrowError(_s("error.notfound.maxpath"))
		End Try
		SetModPath(m_maxpath + "/mod", False)
		m_confighandler = New mxConfigHandler.Create(c_configfile)
		m_confighandler.LoadDefaultLocale()
		m_confighandler.Load()
		m_arghandler = New mxArgumentHandler
		m_arghandler.AddArgImpl(New mxHelpImpl)
		m_arghandler.AddArgImpl(New mxVersionImpl)
		m_arghandler.AddArgImpl(New mxModPathImpl)
		m_arghandler.AddArgImpl(New mxInstallImpl)
		m_arghandler.AddArgImpl(New mxUpdateImpl)
		m_arghandler.AddArgImpl(New mxListImpl)
		m_sourceshandler = New mxSourcesHandler.FromFile(m_sourcesfile)
		If m_sourceshandler = Null
			' Don't throw an error here, the user may be updating the sources (an error will occur otherwise)
			logger.LogWarning(_s("error.load.sources.file", [m_sourcesfile]))
		End If
		Local os:String
		?Win32
			os = "Windows"
		?Linux
			os = "Linux"
		?MacOS
			os = "MacOS"
		?
		m_useragent = "Maximus/" + c_version + " (" + os + "; " + m_locale.GetName() + ")"
	End Method

	Rem
		bbdoc: Called when the application is ended (this includes #ThrowError calls).
		returns: Nothing.
	End Rem
	Method OnExit()
	End Method

	Rem
		bbdoc: Run the application.
		returns: Nothing.
	End Rem
	Method Run()
		Local argimpl:mxArgumentImplementation, isopt:Int
		For Local arg:dIdentifier = EachIn m_args.GetValues()
			argimpl = m_arghandler.GetArgImplFromAlias(arg.GetName())
			isopt = (arg.GetName()[0] = 45)
			If argimpl <> Null
				If isopt = True
					argimpl.SetCallConvention(mxCallConvention.OPTION)
					argimpl.SetArgs(arg)
					argimpl.CheckArgs()
					argimpl.Execute()
				Else
					argimpl.SetCallConvention(mxCallConvention.COMMAND)
					argimpl.SetArgs(arg)
					argimpl.CheckArgs()
					argimpl.Execute()
					Exit
				End If
			Else
				If isopt = True
					ThrowCommonError(mxOptErrors.UNKNOWN, arg.GetName())
				Else
					ThrowCommonError(mxCmdErrors.UNKNOWN, arg.GetName())
				End If
			End If
		Next
		OnExit()
	End Method

	Rem
		bbdoc: Set the application's arguments.
		returns: Nothing.
		about: NOTE: This will resolve to '--help' if the given args are Null.
	End Rem
	Method SetArgs(args:String[])
		If args = Null
			args = ["--help"]
		End If
		m_args = dArgParser.ParseArray(args, False, 1)
	End Method

	Rem
		bbdoc: Set the module path.
		returns: Nothing.
		about: This will throw an error if the module path could not be found.
	End Rem
	Method SetModPath(modpath:String, logchange:Int = True)
		m_modpath = FixPathEnding(modpath, True)
		If FileType(mainapp.m_modpath) = FILETYPE_DIR
			If logchange = True Then logger.LogMessage(_s("message.setmodpath", [mainapp.m_modpath]))
		Else
			ThrowError(_s("error.notfound.modpath", [mainapp.m_modpath]))
		End If
	End Method

	Rem
		bbdoc: Set the sources url.
		returns: Nothing.
	End Rem
	Method SetSourcesUrl(url:String)
		m_sourcesurl = url
	End Method

	Rem
		bbdoc: Set the sources file.
		returns: Nothing.
		about: NOTE: This will throw an error if the path is Null (and, later, a warning will be logged if the path does not exist).
	End Rem
	Method SetSourcesFile(file:String)
		If file = Null
			ThrowError(_s("error.sources.setfile"))
		End If
		m_sourcesfile = file
	End Method

End Type

Rem
	bbdoc: Throw an error (terminating the application).
	returns: Nothing.
End Rem
Function ThrowError(error:String)
	logger.LogError(error)
	mainapp.OnExit()
	End
End Function

Rem
	bbdoc: Get the localized text for the given identifier.
	returns: The translated text.
End Rem
Function _s:String(iden:String, extra:String[] = Null)
	Global replacer:TTextReplacer = New TTextReplacer
	Local ltext:dLocalizedText
	If mainapp.m_locale <> Null Then ltext = mainapp.m_locale.TextFromStructureL(iden)
	If ltext = Null Then ltext = mainapp.m_defaultlocale.TextFromStructureL(iden)
	If ltext <> Null
		replacer.SetString(ltext.GetValue())
		replacer.AutoReplacements("{", "}")
		If extra <> Null
			Local i:Int
			For Local rep:TTextReplacement = EachIn replacer.GetList()
				If i > extra.Length Then Exit
				rep.SetReplacement(extra[i])
				i:+ 1
			Next
		End If
		Return replacer.DoReplacements()
	Else
		DebugLog("Failed to find localized text from structure: ~q" + iden + "~q")
	End If
End Function

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

SuperStrict

Framework brl.blitz
Import brl.standardio
Import brl.maxutil
Import brl.ramstream

Import gman.zipengine
Import htbaapub.rest

Import duct.variables
Import duct.objectmap
Import duct.json
Import duct.locale
Import duct.argparser

Incbin "locales/en.loc"

Include "src/logger.bmx"
Include "src/errors.bmx"
Include "src/config.bmx"
Include "src/dependencies.bmx"
Include "src/module.bmx"
Include "src/sources.bmx"
Include "src/utils.bmx"
Include "src/arghandler.bmx"
Include "src/impl/help.bmx"
Include "src/impl/version.bmx"
Include "src/impl/modpath.bmx"
Include "src/impl/install.bmx"
Include "src/impl/update.bmx"
Include "src/impl/list.bmx"

Global logger:mxLogger = New mxLogger
Global mainapp:mxApp
New mxApp.Create(AppArgs[1..]) ' Skip the first element because it is the program's location
mainapp.Run()

Rem
	bbdoc: Maximus app.
	about: This handles the basic flow of the program (initiation, parsing, command calling, exiting..)
End Rem
Type mxApp

	Const c_version:String = "0.01"
	Const c_configfile:String = "maximus.config"

	Field m_confighandler:mxConfigHandler
	Field m_defaultlocale:dLocale, m_locale:dLocale
	Field m_maxpath:String, m_modpath:String
	Field m_sourcesfile:String = "sources", m_sourcesurl:String = "http://maximus.htbaa.com/module/sources/json"
	Field m_useragent:String

	Field m_args:dIdentifier
	Field m_arghandler:mxArgumentHandler
	Field m_sourceshandler:mxSourcesHandler

	Rem
		bbdoc: Create a new mxApp.
		returns: Itself.
	End Rem
	Method Create:mxApp(args:String[])
		mainapp = Self
		SetArgs(args)
		OnInit()
		Return Self
	End Method

	Rem
		bbdoc: Called when the application is created.
		returns: Nothing.
	End Rem
	Method OnInit()
		Try
			m_maxpath = BlitzMaxPath()
		Catch e:Object
			ThrowError(_s("error.notfound.maxpath"))
		End Try
		SetModPath(m_maxpath + "/mod", False)
		m_confighandler = New mxConfigHandler.Create(c_configfile)
		m_confighandler.LoadDefaultLocale()
		m_confighandler.Load()
		m_arghandler = New mxArgumentHandler
		m_arghandler.AddArgImpl(New mxHelpImpl)
		m_arghandler.AddArgImpl(New mxVersionImpl)
		m_arghandler.AddArgImpl(New mxModPathImpl)
		m_arghandler.AddArgImpl(New mxInstallImpl)
		m_arghandler.AddArgImpl(New mxUpdateImpl)
		m_arghandler.AddArgImpl(New mxListImpl)
		m_sourceshandler = New mxSourcesHandler.FromFile(m_sourcesfile)
		If m_sourceshandler = Null
			' Don't throw an error here, the user may be updating the sources (an error will occur otherwise)
			logger.LogWarning(_s("error.load.sources.file", [m_sourcesfile]))
		End If
		Local os:String
		?Win32
			os = "Windows"
		?Linux
			os = "Linux"
		?MacOS
			os = "MacOS"
		?
		m_useragent = "Maximus/" + c_version + " (" + os + "; " + m_locale.GetName() + ")"
	End Method

	Rem
		bbdoc: Called when the application is ended (this includes #ThrowError calls).
		returns: Nothing.
	End Rem
	Method OnExit()
	End Method

	Rem
		bbdoc: Run the application.
		returns: Nothing.
	End Rem
	Method Run()
		Local argimpl:mxArgumentImplementation, isopt:Int
		For Local arg:dIdentifier = EachIn m_args.GetValues()
			argimpl = m_arghandler.GetArgImplFromAlias(arg.GetName())
			isopt = (arg.GetName()[0] = 45)
			If argimpl <> Null
				If isopt = True
					argimpl.SetCallConvention(mxCallConvention.OPTION)
					argimpl.SetArgs(arg)
					argimpl.CheckArgs()
					argimpl.Execute()
				Else
					argimpl.SetCallConvention(mxCallConvention.COMMAND)
					argimpl.SetArgs(arg)
					argimpl.CheckArgs()
					argimpl.Execute()
					Exit
				End If
			Else
				If isopt = True
					ThrowCommonError(mxOptErrors.UNKNOWN, arg.GetName())
				Else
					ThrowCommonError(mxCmdErrors.UNKNOWN, arg.GetName())
				End If
			End If
		Next
		OnExit()
	End Method

	Rem
		bbdoc: Set the application's arguments.
		returns: Nothing.
		about: NOTE: This will resolve to '--help' if the given args are Null.
	End Rem
	Method SetArgs(args:String[])
		If args = Null
			args = ["--help"]
		End If
		m_args = dArgParser.ParseArray(args, False, 1)
	End Method

	Rem
		bbdoc: Set the module path.
		returns: Nothing.
		about: This will throw an error if the module path could not be found.
	End Rem
	Method SetModPath(modpath:String, logchange:Int = True)
		m_modpath = FixPathEnding(modpath, True)
		If FileType(mainapp.m_modpath) = FILETYPE_DIR
			If logchange = True Then logger.LogMessage(_s("message.setmodpath", [mainapp.m_modpath]))
		Else
			ThrowError(_s("error.notfound.modpath", [mainapp.m_modpath]))
		End If
	End Method

	Rem
		bbdoc: Set the sources url.
		returns: Nothing.
	End Rem
	Method SetSourcesUrl(url:String)
		m_sourcesurl = url
	End Method

	Rem
		bbdoc: Set the sources file.
		returns: Nothing.
		about: NOTE: This will throw an error if the path is Null (and, later, a warning will be logged if the path does not exist).
	End Rem
	Method SetSourcesFile(file:String)
		If file = Null
			ThrowError(_s("error.sources.setfile"))
		End If
		m_sourcesfile = file
	End Method

End Type

Rem
	bbdoc: Throw an error (terminating the application).
	returns: Nothing.
End Rem
Function ThrowError(error:String)
	logger.LogError(error)
	mainapp.OnExit()
	End
End Function

Rem
	bbdoc: Get the localized text for the given identifier.
	returns: The translated text.
End Rem
Function _s:String(iden:String, extra:String[] = Null)
	Global replacer:TTextReplacer = New TTextReplacer
	Local ltext:dLocalizedText
	If mainapp.m_locale <> Null Then ltext = mainapp.m_locale.TextFromStructureL(iden)
	If ltext = Null Then ltext = mainapp.m_defaultlocale.TextFromStructureL(iden)
	If ltext <> Null
		replacer.SetString(ltext.GetValue())
		replacer.AutoReplacements("{", "}")
		If extra <> Null
			Local i:Int
			For Local rep:TTextReplacement = EachIn replacer.GetList()
				If i > extra.Length Then Exit
				rep.SetReplacement(extra[i])
				i:+ 1
			Next
		End If
		Return replacer.DoReplacements()
	Else
		DebugLog("Failed to find localized text from structure: ~q" + iden + "~q")
	End If
End Function


Wrapped more XMLRPC-EPI functions

Wrapped more XMLRPC-EPI functions

'XMLRPC_REQUEST_TYPE
Const xmlrpc_request_none:Int = 0
Const xmlrpc_request_call:Int = 1
Const xmlrpc_request_response:Int = 2

'XMLRPC_VERSION
Const xmlrpc_version_none:Int = 0
Const xmlrpc_version_1_0:Int = 1
Const xmlrpc_version_simple:Int = 2
Const xmlrpc_version_danda:Int = 2
Const xmlrpc_version_soap_1_1:Int = 3

'XMLRPC_VECTOR_TYPE
Const xmlrpc_vector_none:Int = 0
Const xmlrpc_vector_array:Int = 1
Const xmlrpc_vector_mixed:Int = 2
Const xmlrpc_vector_struct:Int = 3

'XMLRPC_VALUE_TYPE
Const xmlrpc_none:Int = 0
Const xmlrpc_empty:Int = 1
Const xmlrpc_base64:Int = 2
Const xmlrpc_boolean:Int = 3
Const xmlrpc_datetime:Int = 4
Const xmlrpc_double:Int = 5
Const xmlrpc_int:Int = 6
Const xmlrpc_string:Int = 7
Const xmlrpc_vector:Int = 8

'XMLRPC_VALUE_TYPE_EASY
Const xmlrpc_type_none:Int = 0
Const xmlrpc_type_empty:Int = 1
Const xmlrpc_type_base64:Int = 2
Const xmlrpc_type_boolean:Int = 3
Const xmlrpc_type_datetime:Int = 4
Const xmlrpc_type_double:Int = 5
Const xmlrpc_type_int:Int = 6
Const xmlrpc_type_string:Int = 7
Const xmlrpc_type_array:Int = 8
Const xmlrpc_type_mixed:Int = 9
Const xmlrpc_type_struct:Int = 10

'XMLRPC_ERROR_CODE
Const xmlrpc_error_none:Int = 0
Const xmlrpc_error_parse_xml_syntax:Int = -32700
Const xmlrpc_error_parse_unknown_encoding:Int = -32701
Const xmlrpc_error_parse_bad_encoding:Int = -32702
Const xmlrpc_error_invalid_xmlrpc:Int = -32600
Const xmlrpc_error_unknown_method:Int = -32601
Const xmlrpc_error_invalid_params:Int = -32602
Const xmlrpc_error_internal_server:Int = -32603
Const xmlrpc_error_application:Int = -32500
Const xmlrpc_error_system:Int = -32400
Const xmlrpc_error_transport:Int = -32300

'XMLRPC_SERVER_VALIDATION
Const validation_none:Int = 0
Const validation_all:Int = 1
Const validation_if_defined:Int = 2

'XMLRPC_CASE_COMPARISON
Const xmlrpc_case_insensitive:Int = 0
Const xmlrpc_case_sensitive:Int = 1

'XMLRPC_CASE
Const xmlrpc_case_exact:Int = 0
Const xmlrpc_case_lower:Int = 1
Const xmlrpc_case_upper:Int = 2

Extern
	Function XMLRPC_Create_STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS:Byte Ptr(version:Int)
'	Function XMLRPC_Delete_Request_Output_Options(options:Byte Ptr)
End Extern

Extern "C"

	Function XMLRPC_RequestNew:Byte Ptr()
	Function XMLRPC_RequestFree(request:Byte Ptr, bFreeIO:Int)
	Function XMLRPC_RequestSetMethodName:Byte Ptr(request:Byte Ptr, methodName:Byte Ptr)
	Function XMLRPC_RequestGetMethodName:Byte Ptr(request:Byte Ptr)
	Function XMLRPC_RequestSetRequestType:Int(request:Byte Ptr, iType:Int)
	Function XMLRPC_RequestGetRequestType:Int(request:Byte Ptr)
	Function XMLRPC_RequestSetData:Byte Ptr(request:Byte Ptr, data:Byte Ptr)
	Function XMLRPC_RequestGetData:Byte Ptr(request:Byte Ptr)

	Function XMLRPC_REQUEST_ToXML:Byte Ptr(request:Byte Ptr, buf_len:Byte Ptr)
	Function XMLRPC_REQUEST_FromXML:Byte Ptr(in_buf:Byte Ptr, length:Int, in_options:Byte Ptr)
	Function XMLRPC_VALUE_ToXML:Byte Ptr(val:Int, buf_len:Byte Ptr)
	Function XMLRPC_VALUE_FromXML:Byte Ptr(in_buf:Byte Ptr, length:Int, in_options:Byte Ptr)

	Function XMLRPC_RequestSetOutputOptions:Byte Ptr(request:Byte Ptr, output:Byte Ptr)
	Function XMLRPC_RequestGetOutputOptions:Byte Ptr(request:Byte Ptr)

	Function XMLRPC_CreateVector:Byte Ptr(id:String, iType:Int)
	Function XMLRPC_AddValueToVector:Int(target:Byte Ptr, source:Byte Ptr)
	Function XMLRPC_VectorSize:Int(value:Byte Ptr)
	Function XMLRPC_VectorRewind:Byte Ptr(value:Byte Ptr)
	Function XMLRPC_VectorNext:Byte Ptr(value:Byte Ptr)

	Function XMLRPC_CreateValueBoolean:Byte Ptr(id:Byte Ptr, truth:Int)
	Function XMLRPC_CreateValueBase64:Byte Ptr(id:Byte Ptr, s:Byte Ptr, length:Int)
	Function XMLRPC_CreateValueDateTime:Byte Ptr(id:Byte Ptr, time:Long)
	Function XMLRPC_CreateValueDateTime_ISO8601:Byte Ptr(id:Byte Ptr, s:Byte Ptr)
	Function XMLRPC_CreateValueDouble:Byte Ptr(id:Byte Ptr, f:Double)
	Function XMLRPC_CreateValueInt:Byte Ptr(id:Byte Ptr, i:Int)
	Function XMLRPC_CreateValueEmpty:Byte Ptr()
	Function XMLRPC_CreateValueString:Byte Ptr(id:Byte Ptr, s:Byte Ptr, length:Int)

	Function XMLRPC_RequestSetError:Int(request:Byte Ptr, error:Int)
	Function XMLRPC_RequestGetError:Int(request:Byte Ptr)

	Function XMLRPC_VectorGetValueWithID_Case:Byte Ptr(vector:Byte Ptr, id:Byte Ptr, id_case:Int)

	Function XMLRPC_GetValueType:Int(v:Byte Ptr)
	'XMLRPC_VALUE_TYPE_EASY XMLRPC_GetValueTypeEasy(XMLRPC_VALUE v);
	'XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE v);

	Function XMLRPC_GetValueString:Byte Ptr(value:Byte Ptr)
	Function XMLRPC_GetValueStringLen:Int(value:Byte Ptr)
	Function XMLRPC_GetValueInt:Int(value:Byte Ptr)
	Function XMLRPC_GetValueBoolean:Int(value:Byte Ptr)
	Function XMLRPC_GetValueDouble:Double(value:Byte Ptr)
	Function XMLRPC_GetValueBase64:Byte Ptr(value:Byte Ptr)
	Function XMLRPC_GetValueDateTime:Long(value:Byte Ptr)
	Function XMLRPC_GetValueDateTime_ISO8601:Byte Ptr(value:Byte Ptr)
	Function XMLRPC_GetValueID:Byte Ptr(value:Byte Ptr)

	Function XMLRPC_ValueIsFault:Int(value:Byte Ptr)
	Function XMLRPC_ResponseIsFault(response:Byte Ptr)
	Function XMLRPC_GetValueFaultCode:Int(value:Byte Ptr)
	Function XMLRPC_GetResponseFaultCode:Int(response:Byte Ptr)
	Function XMLRPC_GetValueFaultString:Byte Ptr(value:Byte Ptr)
	Function XMLRPC_GetResponseFaultString:Byte Ptr(response:Byte Ptr)
	Function XMLRPC_Free(mem:Byte Ptr)
	Function XMLRPC_GetVersionString:Byte Ptr()
Rem

XMLRPC_VALUE XMLRPC_UtilityCreateFault(int fault_code, const char* fault_string);

/* Get Values */
#define XMLRPC_VectorGetStringWithID(vector, id) XMLRPC_GetValueString(XMLRPC_VectorGetValueWithID(vector, id))
#define XMLRPC_VectorGetBase64WithID(vector, id) XMLRPC_GetValueBase64(XMLRPC_VectorGetValueWithID(vector, id))
#define XMLRPC_VectorGetDateTimeWithID(vector, id) XMLRPC_GetValueDateTime(XMLRPC_VectorGetValueWithID(vector, id))
#define XMLRPC_VectorGetDoubleWithID(vector, id) XMLRPC_GetValueDouble(XMLRPC_VectorGetValueWithID(vector, id))
#define XMLRPC_VectorGetIntWithID(vector, id) XMLRPC_GetValueInt(XMLRPC_VectorGetValueWithID(vector, id))
#define XMLRPC_VectorGetBooleanWithID(vector, id) XMLRPC_GetValueBoolean(XMLRPC_VectorGetValueWithID(vector, id))
Endrem
End Extern

Function XMLRPC_VectorGetValueWithID:Byte Ptr(vector:Byte Ptr, id:String)
	Return XMLRPC_VectorGetValueWithID_Case(vector, id.ToCString(), xmlrpc_case_sensitive)
End Function

Function XMLRPC_VectorAppendString:Int(vector:Byte Ptr, id:String, s:String, length:Int = 0)
	Return XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueString(id.ToCString(), s.ToCString(), length))
End Function

Function XMLRPC_VectorAppendBase64:Int(vector:Byte Ptr, id:String, s:String, length:Int = 0)
	Return XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueBase64(id.ToCString(), s.ToCString(), length))
End Function

Function XMLRPC_VectorAppendDateTime:Int(vector:Byte Ptr, id:Byte Ptr, time:Long)
	Return XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDateTime(id, time))
End Function

Function XMLRPC_VectorAppendDateTime_ISO8601:Int(vector:Byte Ptr, id:String, s:String)
	Return XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDateTime_ISO8601(id.ToCString(), s.ToCString()))
End Function

Function XMLRPC_VectorAppendDouble:Int(vector:Byte Ptr, id:String, f:Double)
	Return XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDouble(id.ToCString(), f))
End Function

Function XMLRPC_VectorAppendInt:Int(vector:Byte Ptr, id:String, i:Int)
	Return XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueInt(id.ToCString(), i))
End Function

Function XMLRPC_VectorAppendBoolean:Int(vector:Byte Ptr, id:String, i:Int)
	Return XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueBoolean(id.ToCString(), i))
End Function
rem
/****s* VALUE/XMLRPC_REQUEST_INPUT_OPTIONS
 * NAME
 *   XMLRPC_REQUEST_INPUT_OPTIONS
 * NOTES
 *   Defines options for reading in xml data
 * SEE ALSO
 *   XMLRPC_VERSION
 *   XML_ELEM_INPUT_OPTIONS
 *   XMLRPC_REQUEST_From_XML ()
 * SOURCE
 */
typedef struct _xmlrpc_request_input_options {
   STRUCT_XML_ELEM_INPUT_OPTIONS  xml_elem_opts;  /* xml_element specific output options */
} STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS, *XMLRPC_REQUEST_INPUT_OPTIONS;
/******/

/****s* VALUE/XMLRPC_ERROR
 * NAME
 *   XMLRPC_ERROR
 * NOTES
 *   For the reporting and handling of errors
 * SOURCE
 */
typedef struct _xmlrpc_error {
   XMLRPC_ERROR_CODE      code;
   STRUCT_XML_ELEM_ERROR  xml_elem_error;  /* xml_element errors (parser errors) */
} STRUCT_XMLRPC_ERROR, *XMLRPC_ERROR;
/******/
endrem

Rem
	String conversion stuff below. Shamelessly stolen from Brucey :-)
End Rem

Extern
	Function _strlen:Int(s:Byte Ptr) = "strlen"
End Extern

' Convert from Max to UTF8
Function convertISO8859toUTF8:String(text:String)
	If Not text Then
		Return ""
	End If

	Local l:Int = text.length
	If l = 0 Then
		Return ""
	End If

	Local count:Int = 0
	Local s:Byte[] = New Byte[l * 3]

	For Local i:Int = 0 Until l
		Local char:Int = text[i]

		If char < 128 Then
			s[count] = char
			count:+ 1
			Continue
		Else If char<2048
			s[count] = char/64 | 192
			count:+ 1
			s[count] = char Mod 64 | 128
			count:+ 1
			Continue
		Else
			s[count] =  char/4096 | 224
			count:+ 1
			s[count] = char/64 Mod 64 | 128
			count:+ 1
			s[count] = char Mod 64 | 128
			count:+ 1
			Continue
		EndIf

	Next

	Return String.fromBytes(s, count)
End Function

' Convert from UTF8 to Max
Function convertUTF8toISO8859:String(s:Byte Ptr)

	Local l:Int = _strlen(s)

	Local b:Short[] = New Short[l]
	Local bc:Int = -1
	Local c:Int
	Local d:Int
	Local e:Int
	For Local i:Int = 0 Until l

		bc:+1
		c = s[i]
		If c<128
			b[bc] = c
			Continue
		End If
		i:+1
		d=s[i]
		If c<224
			b[bc] = (c-192)*64+(d-128)
			Continue
		End If
		i:+1
		e = s[i]
		If c < 240
			b[bc] = (c-224)*4096+(d-128)*64+(e-128)
			If b[bc] = 8233 Then
				b[bc] = 10
			End If
			Continue
		End If
	Next

	Return String.fromshorts(b, bc + 1)
End Function

Function sizedUTF8toISO8859:String(s:Byte Ptr, size:Int)

	Local l:Int = size
	Local b:Short[] = New Short[l]
	Local bc:Int = -1
	Local c:Int
	Local d:Int
	Local e:Int
	For Local i:Int = 0 Until l

		c = s[i]
		If c = 0 Continue

		bc:+1
		If c<128
			b[bc] = c
			Continue
		End If
		i:+1
		d=s[i]
		If c<224
			b[bc] = (c-192)*64+(d-128)
			Continue
		End If
		i:+1
		e = s[i]
		If c < 240
			b[bc] = (c-224)*4096+(d-128)*64+(e-128)
			If b[bc] = 8233 Then
				b[bc] = 10
			End If
			Continue
		End If
	Next

	Return String.fromshorts(b, bc + 1)
End Function

'XMLRPC_REQUEST_TYPE
Const xmlrpc_request_none:Int = 0
Const xmlrpc_request_call:Int = 1
Const xmlrpc_request_response:Int = 2

'XMLRPC_VERSION
Const xmlrpc_version_none:Int = 0
Const xmlrpc_version_1_0:Int = 1
Const xmlrpc_version_simple:Int = 2
Const xmlrpc_version_danda:Int = 2
Const xmlrpc_version_soap_1_1:Int = 3

'XMLRPC_VECTOR_TYPE
Const xmlrpc_vector_none:Int = 0
Const xmlrpc_vector_array:Int = 1
Const xmlrpc_vector_mixed:Int = 2
Const xmlrpc_vector_struct:Int = 3

'XMLRPC_VALUE_TYPE
Const xmlrpc_none:Int = 0
Const xmlrpc_empty:Int = 1
Const xmlrpc_base64:Int = 2
Const xmlrpc_boolean:Int = 3
Const xmlrpc_datetime:Int = 4
Const xmlrpc_double:Int = 5
Const xmlrpc_int:Int = 6
Const xmlrpc_string:Int = 7
Const xmlrpc_vector:Int = 8

'XMLRPC_VALUE_TYPE_EASY
Const xmlrpc_type_none:Int = 0
Const xmlrpc_type_empty:Int = 1
Const xmlrpc_type_base64:Int = 2
Const xmlrpc_type_boolean:Int = 3
Const xmlrpc_type_datetime:Int = 4
Const xmlrpc_type_double:Int = 5
Const xmlrpc_type_int:Int = 6
Const xmlrpc_type_string:Int = 7
Const xmlrpc_type_array:Int = 8
Const xmlrpc_type_mixed:Int = 9
Const xmlrpc_type_struct:Int = 10

'XMLRPC_ERROR_CODE
Const xmlrpc_error_none:Int = 0
Const xmlrpc_error_parse_xml_syntax:Int = -32700
Const xmlrpc_error_parse_unknown_encoding:Int = -32701
Const xmlrpc_error_parse_bad_encoding:Int = -32702
Const xmlrpc_error_invalid_xmlrpc:Int = -32600
Const xmlrpc_error_unknown_method:Int = -32601
Const xmlrpc_error_invalid_params:Int = -32602
Const xmlrpc_error_internal_server:Int = -32603
Const xmlrpc_error_application:Int = -32500
Const xmlrpc_error_system:Int = -32400
Const xmlrpc_error_transport:Int = -32300

'XMLRPC_SERVER_VALIDATION
Const validation_none:Int = 0
Const validation_all:Int = 1
Const validation_if_defined:Int = 2

'XMLRPC_CASE_COMPARISON
Const xmlrpc_case_insensitive:Int = 0
Const xmlrpc_case_sensitive:Int = 1

'XMLRPC_CASE
Const xmlrpc_case_exact:Int = 0
Const xmlrpc_case_lower:Int = 1
Const xmlrpc_case_upper:Int = 2

Extern
	Function XMLRPC_Create_STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS:Byte Ptr(version:Int)
'	Function XMLRPC_Delete_Request_Output_Options(options:Byte Ptr)
End Extern

Extern "C"

	Function XMLRPC_RequestNew:Byte Ptr()
	Function XMLRPC_RequestFree(request:Byte Ptr, bFreeIO:Int)
	Function XMLRPC_RequestSetMethodName:Byte Ptr(request:Byte Ptr, methodName:Byte Ptr)
	Function XMLRPC_RequestGetMethodName:Byte Ptr(request:Byte Ptr)
	Function XMLRPC_RequestSetRequestType:Int(request:Byte Ptr, iType:Int)
	Function XMLRPC_RequestGetRequestType:Int(request:Byte Ptr)
	Function XMLRPC_RequestSetData:Byte Ptr(request:Byte Ptr, data:Byte Ptr)
	Function XMLRPC_RequestGetData:Byte Ptr(request:Byte Ptr)

	Function XMLRPC_REQUEST_ToXML:Byte Ptr(request:Byte Ptr, buf_len:Byte Ptr)
	Function XMLRPC_REQUEST_FromXML:Byte Ptr(in_buf:Byte Ptr, length:Int, in_options:Byte Ptr)
	Function XMLRPC_VALUE_ToXML:Byte Ptr(val:Int, buf_len:Byte Ptr)
	Function XMLRPC_VALUE_FromXML:Byte Ptr(in_buf:Byte Ptr, length:Int, in_options:Byte Ptr)

	Function XMLRPC_RequestSetOutputOptions:Byte Ptr(request:Byte Ptr, output:Byte Ptr)
	Function XMLRPC_RequestGetOutputOptions:Byte Ptr(request:Byte Ptr)

	Function XMLRPC_CreateVector:Byte Ptr(id:String, iType:Int)
	Function XMLRPC_AddValueToVector:Int(target:Byte Ptr, source:Byte Ptr)
	Function XMLRPC_VectorSize:Int(value:Byte Ptr)
	Function XMLRPC_VectorRewind:Byte Ptr(value:Byte Ptr)
	Function XMLRPC_VectorNext:Byte Ptr(value:Byte Ptr)

	Function XMLRPC_CreateValueBoolean:Byte Ptr(id:Byte Ptr, truth:Int)
	Function XMLRPC_CreateValueBase64:Byte Ptr(id:Byte Ptr, s:Byte Ptr, length:Int)
	Function XMLRPC_CreateValueDateTime:Byte Ptr(id:Byte Ptr, time:Long)
	Function XMLRPC_CreateValueDateTime_ISO8601:Byte Ptr(id:Byte Ptr, s:Byte Ptr)
	Function XMLRPC_CreateValueDouble:Byte Ptr(id:Byte Ptr, f:Double)
	Function XMLRPC_CreateValueInt:Byte Ptr(id:Byte Ptr, i:Int)
	Function XMLRPC_CreateValueEmpty:Byte Ptr()
	Function XMLRPC_CreateValueString:Byte Ptr(id:Byte Ptr, s:Byte Ptr, length:Int)

	Function XMLRPC_RequestSetError:Int(request:Byte Ptr, error:Int)
	Function XMLRPC_RequestGetError:Int(request:Byte Ptr)

	Function XMLRPC_VectorGetValueWithID_Case:Byte Ptr(vector:Byte Ptr, id:Byte Ptr, id_case:Int)

	Function XMLRPC_GetValueType:Int(v:Byte Ptr)
	'XMLRPC_VALUE_TYPE_EASY XMLRPC_GetValueTypeEasy(XMLRPC_VALUE v);
	'XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE v);

	Function XMLRPC_GetValueString:Byte Ptr(value:Byte Ptr)
	Function XMLRPC_GetValueStringLen:Int(value:Byte Ptr)
	Function XMLRPC_GetValueInt:Int(value:Byte Ptr)
	Function XMLRPC_GetValueBoolean:Int(value:Byte Ptr)
	Function XMLRPC_GetValueDouble:Double(value:Byte Ptr)
	Function XMLRPC_GetValueBase64:Byte Ptr(value:Byte Ptr)
	Function XMLRPC_GetValueDateTime:Long(value:Byte Ptr)
	Function XMLRPC_GetValueDateTime_ISO8601:Byte Ptr(value:Byte Ptr)
	Function XMLRPC_GetValueID:Byte Ptr(value:Byte Ptr)
Rem
/* Get Values */

#define XMLRPC_VectorGetStringWithID(vector, id) XMLRPC_GetValueString(XMLRPC_VectorGetValueWithID(vector, id))
#define XMLRPC_VectorGetBase64WithID(vector, id) XMLRPC_GetValueBase64(XMLRPC_VectorGetValueWithID(vector, id))
#define XMLRPC_VectorGetDateTimeWithID(vector, id) XMLRPC_GetValueDateTime(XMLRPC_VectorGetValueWithID(vector, id))
#define XMLRPC_VectorGetDoubleWithID(vector, id) XMLRPC_GetValueDouble(XMLRPC_VectorGetValueWithID(vector, id))
#define XMLRPC_VectorGetIntWithID(vector, id) XMLRPC_GetValueInt(XMLRPC_VectorGetValueWithID(vector, id))
#define XMLRPC_VectorGetBooleanWithID(vector, id) XMLRPC_GetValueBoolean(XMLRPC_VectorGetValueWithID(vector, id))
Endrem
End Extern

Function XMLRPC_VectorGetValueWithID:Byte Ptr(vector:Byte Ptr, id:String)
	Return XMLRPC_VectorGetValueWithID_Case(vector, id.ToCString(), xmlrpc_case_sensitive)
End Function

Function XMLRPC_VectorAppendString:Int(vector:Byte Ptr, id:String, s:String, length:Int = 0)
	Return XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueString(id.ToCString(), s.ToCString(), length))
End Function

Function XMLRPC_VectorAppendBase64:Int(vector:Byte Ptr, id:String, s:String, length:Int = 0)
	Return XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueBase64(id.ToCString(), s.ToCString(), length))
End Function

Function XMLRPC_VectorAppendDateTime:Int(vector:Byte Ptr, id:Byte Ptr, time:Long)
	Return XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDateTime(id, time))
End Function

Function XMLRPC_VectorAppendDateTime_ISO8601:Int(vector:Byte Ptr, id:String, s:String)
	Return XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDateTime_ISO8601(id.ToCString(), s.ToCString()))
End Function

Function XMLRPC_VectorAppendDouble:Int(vector:Byte Ptr, id:String, f:Double)
	Return XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDouble(id.ToCString(), f))
End Function

Function XMLRPC_VectorAppendInt:Int(vector:Byte Ptr, id:String, i:Int)
	Return XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueInt(id.ToCString(), i))
End Function

Function XMLRPC_VectorAppendBoolean:Int(vector:Byte Ptr, id:String, i:Int)
	Return XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueBoolean(id.ToCString(), i))
End Function
rem
/****s* VALUE/XMLRPC_REQUEST_INPUT_OPTIONS
 * NAME
 *   XMLRPC_REQUEST_INPUT_OPTIONS
 * NOTES
 *   Defines options for reading in xml data
 * SEE ALSO
 *   XMLRPC_VERSION
 *   XML_ELEM_INPUT_OPTIONS
 *   XMLRPC_REQUEST_From_XML ()
 * SOURCE
 */
typedef struct _xmlrpc_request_input_options {
   STRUCT_XML_ELEM_INPUT_OPTIONS  xml_elem_opts;  /* xml_element specific output options */
} STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS, *XMLRPC_REQUEST_INPUT_OPTIONS;
/******/

/****s* VALUE/XMLRPC_ERROR
 * NAME
 *   XMLRPC_ERROR
 * NOTES
 *   For the reporting and handling of errors
 * SOURCE
 */
typedef struct _xmlrpc_error {
   XMLRPC_ERROR_CODE      code;
   STRUCT_XML_ELEM_ERROR  xml_elem_error;  /* xml_element errors (parser errors) */
} STRUCT_XMLRPC_ERROR, *XMLRPC_ERROR;
/******/
endrem

Rem
	String conversion stuff below. Shamelessly stolen from Brucey :-)
End Rem

Extern
	Function _strlen:Int(s:Byte Ptr) = "strlen"
End Extern

' Convert from Max to UTF8
Function convertISO8859toUTF8:String(text:String)
	If Not text Then
		Return ""
	End If

	Local l:Int = text.length
	If l = 0 Then
		Return ""
	End If

	Local count:Int = 0
	Local s:Byte[] = New Byte[l * 3]

	For Local i:Int = 0 Until l
		Local char:Int = text[i]

		If char < 128 Then
			s[count] = char
			count:+ 1
			Continue
		Else If char<2048
			s[count] = char/64 | 192
			count:+ 1
			s[count] = char Mod 64 | 128
			count:+ 1
			Continue
		Else
			s[count] =  char/4096 | 224
			count:+ 1
			s[count] = char/64 Mod 64 | 128
			count:+ 1
			s[count] = char Mod 64 | 128
			count:+ 1
			Continue
		EndIf

	Next

	Return String.fromBytes(s, count)
End Function

' Convert from UTF8 to Max
Function convertUTF8toISO8859:String(s:Byte Ptr)

	Local l:Int = _strlen(s)

	Local b:Short[] = New Short[l]
	Local bc:Int = -1
	Local c:Int
	Local d:Int
	Local e:Int
	For Local i:Int = 0 Until l

		bc:+1
		c = s[i]
		If c<128
			b[bc] = c
			Continue
		End If
		i:+1
		d=s[i]
		If c<224
			b[bc] = (c-192)*64+(d-128)
			Continue
		End If
		i:+1
		e = s[i]
		If c < 240
			b[bc] = (c-224)*4096+(d-128)*64+(e-128)
			If b[bc] = 8233 Then
				b[bc] = 10
			End If
			Continue
		End If
	Next

	Return String.fromshorts(b, bc + 1)
End Function

Function sizedUTF8toISO8859:String(s:Byte Ptr, size:Int)

	Local l:Int = size
	Local b:Short[] = New Short[l]
	Local bc:Int = -1
	Local c:Int
	Local d:Int
	Local e:Int
	For Local i:Int = 0 Until l

		c = s[i]
		If c = 0 Continue

		bc:+1
		If c<128
			b[bc] = c
			Continue
		End If
		i:+1
		d=s[i]
		If c<224
			b[bc] = (c-192)*64+(d-128)
			Continue
		End If
		i:+1
		e = s[i]
		If c < 240
			b[bc] = (c-224)*4096+(d-128)*64+(e-128)
			If b[bc] = 8233 Then
				b[bc] = 10
			End If
			Continue
		End If
	Next

	Return String.fromshorts(b, bc + 1)
End Function


Now builds again

Now builds again

e0c09b3b76e88124897f4592b7415ac62be9f05c broke the build by removing a bit
too much

refs gh-15, gh-6

Rem
	bbdoc: Maximus 'install' argument implementation.
End Rem
Type mxInstallImpl Extends dArgumentImplementation

	Field m_instmap:dObjectMap
	Field m_depcheckmap:dObjectMap
	Field m_unmanagedmap:dObjectMap
	Field m_nobuild:Int = False, m_nounpack:Int = False, m_keeptemp:Int = False
	Field m_nothreaded:Int = False, m_makedocs:Int = False, m_forceinstall:Int = False

	Method New()
		init(["install"])
		m_instmap = New dObjectMap
		m_depcheckmap = New dObjectMap
		m_unmanagedmap = New dObjectMap
	End Method

	Rem
		bbdoc: Check the current arguments for errors (according to the specific implementation).
		returns: Nothing.
		about: This method will throw an error if the arguments are invalid.
	End Rem
	Method CheckArgs()
		If GetArgumentCount() = 0
			ThrowCommonError(mxCmdErrors.REQUIRESPARAMS, m_args.GetName())
		End If
		CheckOptions()
	End Method

	Rem
		bbdoc: Get a string describing the typical usage of the argument.
		returns: A string describing the typical usage of the argument.
	End Rem
	Method GetUsage:String()
		Return _s("arg.install.usage")
	End Method

	Rem
		bbdoc: Execute the implementation's operation.
		returns: Nothing.
	End Rem
	Method Execute()
		m_instmap.Clear()
		m_depcheckmap.Clear()
		m_unmanagedmap.Clear()
		If mainapp.m_sourceshandler
			Local nfounds:dObjectMap = New dObjectMap
			For Local svar:dStringVariable = EachIn m_args
				Local verid:String = svar.Get(), modid:String = mxModUtils.GetIDFromVerID(verid)
				Local modul:mxModule = mainapp.m_sourceshandler.GetModuleWithID(modid)
				If modul
					Local instmod:mxInstModule = New mxInstModule.Create(verid, modul)
					If instmod.SetVersionFromVerID(verid)
						m_instmap._Insert(modid, instmod)
					Else
						ThrowError(_s("arg.install.notfound.version", [modid, mxModUtils.GetVersionFromVerID(verid)]))
					End If
				Else
					nfounds._Insert(modid, modid)
				End If
			Next
			If nfounds.Count() > 0
				logger.LogError(_s("arg.install.notfound.instmods"))
				Local a:String
				For Local b:String = EachIn nfounds.ValueEnumerator()
					a:+ b + " "
				Next
				a = a[..a.Length - 1]
				logger.LogMessage("~t" + a)
				Return
			End If
			If CheckDependencies()
				'Make sure we only install/update managed modules
				If CheckVersions()
					DoInstall()
				End If
			End If
		Else
			ThrowError(_s("error.install.nosources"))
		End If
	End Method

	Rem
		bbdoc: Check the options given to the command.
		returns: Nothing.
	End Rem
	Method CheckOptions()
		m_nobuild = False; m_nounpack = False; m_keeptemp = False
		m_nothreaded = False; m_makedocs = False
		For Local opt:dIdentifier = EachIn m_args
			Select opt.GetName().ToLower()
				Case "-nobuild" m_nobuild = True
				Case "-nounpack" m_nounpack = True
				Case "-keeptemp" m_keeptemp = True
				Case "-nothreaded" m_nothreaded = True
				Case "-makedocs" m_makedocs = True
				Case "-force" m_forceinstall = True
				Default ThrowCommonError(mxOptErrors.UNKNOWN, opt.GetName())
			End Select
		Next
	End Method

	Rem
		bbdoc: Check for version conflictions and warn the user.
		returns: True if the installtion should continue, or False if the user halted the operation.
	End Rem
	Method CheckVersions:Int()
		For Local instmod:mxInstModule = EachIn m_instmap.ValueEnumerator()
			Local ver:String = mxModUtils.GetInstalledVersionFromVerID(instmod.GetVerID())
			'Make sure we skip unmanaged modules
			If ver = "unmanaged"
				m_instmap._Remove(instmod.GetID())
				m_unmanagedmap._Insert(instmod.GetID(), instmod)
			End If
		Next
		Return True
	End Method

	Rem
		bbdoc: Check the dependencies for the modules to be installed.
		returns: Nothing.
	End Rem
	Method CheckDependencies:Int()
		Local nfounds:dObjectMap = New dObjectMap
		For Local instmod:mxInstModule = EachIn m_instmap.ValueEnumerator()
			CheckModuleDependencies(instmod, nfounds)
		Next
		If nfounds.Count() > 0
			logger.LogError(_s("arg.install.notfound.instdeps"))
			Local a:String
			For Local b:String = EachIn nfounds.ValueEnumerator()
				a:+ b + " "
			Next
			a = a[..a.Length - 1]
			logger.LogMessage("~t" + a)
			If m_forceinstall
				m_nobuild = True
				Local resp:String = Input(_s("arg.install.missingdeps") + " ").ToLower()
				If resp = "y" Or resp = "yes"
					Return True
				End If
			End If
			Return False
		End If
		Return True
	End Method

	Rem
		bbdoc: Check the dependencies for the given module and adapt the install list accordingly.
		returns: Nothing.
	End Rem
	Method CheckModuleDependencies(instmod:mxInstModule, nfounds:dObjectMap)
		Local addlist:TListEx = instmod.CheckCompliance()
		If addlist
			For Local modid:String = EachIn addlist
				If Not m_depcheckmap._Contains(modid)
					Local dmodul:mxModule = mainapp.m_sourceshandler.GetModuleWithID(modid)
					m_depcheckmap._Insert(modid, modid)
					If dmodul
						If Not m_instmap._Contains(modid)
							Local dinstmod:mxInstModule = New mxInstModule.Create(modid, dmodul)
							If instmod.GetVersionName().ToLower() = "dev"
								dinstmod.SetVersionFromName("dev")
							Else
								dinstmod.SetVersion(Null)
							End If
							m_instmap._Insert(modid, dinstmod)
							CheckModuleDependencies(dinstmod, nfounds)
						End If
					Else
						nfounds._Insert(modid, modid)
					End If
				End If
			Next
		End If
	End Method

	Rem
		bbdoc: Do the actual installing.
		returns: Nothing.
	End Rem
	Method DoInstall()
		If m_instmap.Count() > 0
			Local a:String, instmod:mxInstModule

			If m_unmanagedmap.Count()
				logger.LogMessage(_s("arg.install.modulestoskip"))
				For instmod = EachIn m_unmanagedmap.ValueEnumerator()
					a:+instmod.GetVerID() + " "
				Next
				a = a[..a.Length - 1]
				logger.LogMessage("~t" + a)
				a = ""
			End If

			logger.LogMessage(_s("arg.install.modulestoinstall"))
			For instmod = EachIn m_instmap.ValueEnumerator()
				a:+ instmod.GetVerID() + " "
			Next
			a = a[..a.Length - 1]
			logger.LogMessage("~t" + a)
			Local resp:String = Input(_s("arg.install.continuewithinstall") + " ").ToLower()
			If resp = "y" Or resp = "yes"
				For instmod = EachIn m_instmap.ValueEnumerator()
					instmod.FetchSourceArchive()
				Next
				If Not m_nounpack
					For instmod = EachIn m_instmap.ValueEnumerator()
						instmod.Unpack()
					Next
				Else
					logger.LogMessage(_s("message.skipping.unpack"))
				End If
				If Not m_keeptemp Then DeleteDir("tmp/", True)
				If Not m_nobuild
					Local scopes:dObjectMap = New dObjectMap
					For instmod = EachIn m_instmap.ValueEnumerator()
						scopes._Insert(instmod.GetModuleScope(), instmod.GetModuleScope())
					Next
					For Local scope:String = EachIn scopes.ValueEnumerator()
						If mxBMKUtils.MakeMods(scope, m_nothreaded ~ 1) <> 0
							ThrowError(_s("error.install.build", [scope]))
						End If
					Next
					If m_makedocs
						'For instmod = EachIn m_instmap.ValueEnumerator()
						'	mxModUtils.DocMods(instmod.GetID())
						'Next
						' Can't build docs for individual modules yet
						mxModUtils.DocMods()
					End If
				Else
					logger.LogMessage(_s("message.skipping.install"))
				End If
			End If
		Else
			logger.LogMessage(_s("arg.install.nomodulestoinstall"))
			If m_unmanagedmap.Count()
				logger.LogMessage(_s("arg.install.modulestoskip"))
				Local a:String
				For Local instmod:mxInstModule = EachIn m_unmanagedmap.ValueEnumerator()
					a:+instmod.GetVerID() + " "
				Next
				a = a[..a.Length - 1]
				logger.LogMessage("~t" + a)
			End If
		End If
	End Method

End Type

Rem
	bbdoc: Maximus install info module.
	about: This type is used for temporary install information on modules.
End Rem
Type mxInstModule

	Field m_id:String
	Field m_version:mxModuleVersion
	Field m_module:mxModule

	Rem
		bbdoc: Create a new mxInstModule.
		returns: Itself.
	End Rem
	Method Create:mxInstModule(verid:String, modul:mxModule)
		SetID(mxModUtils.GetIDFromVerID(verid))
		SetModule(modul)
		Return Self
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the modid to be installed.
		returns: Nothing.
	End Rem
	Method SetID(id:String)
		m_id = id
	End Method

	Rem
		bbdoc: Get the modid to be installed.
		returns: The modid to be installed.
	End Rem
	Method GetID:String()
		Return m_id
	End Method

	Rem
		bbdoc: Set the version from the given versioned-module name (e.g. "foo.bar/1.02").
		returns: True if the version was set, or False if the version could not be found (was not found).
		about: The latest version will be set if the given id is Null (or if it isn't versioned (e.g. "foo.bar", would result in the latest version being selected)).
	End Rem
	Method SetVersionFromVerID:Int(verid:String)
		Return SetVersionFromName(mxModUtils.GetVersionFromVerID(verid))
	End Method

	Rem
		bbdoc: Set the version to be installed from the given version name (e.g. "1.02").
		returns: True if the version was set, or False if the version could not be set (was not found).
		about: The latest version will be set if the given name is Null.
	End Rem
	Method SetVersionFromName:Int(name:String)
		If Not name
			SetVersion(Null) ' Set the latest version (the version wasn't forced)
			Return True
		Else
			Local ver:mxModuleVersion = m_module.GetVersionWithName(name)
			If ver
				SetVersion(ver)
				Return True
			End If
		End If
		Return False
	End Method

	Rem
		bbdoc: Set the install version.
		returns: Nothing.
		about: If the given value is Null, it will be automatically set to the latest (non-development) version.
	End Rem
	Method SetVersion(version:mxModuleVersion)
		m_version = version
		If Not m_version
			m_version = m_module.GetLatestVersion()
		End If
	End Method

	Rem
		bbdoc: Get the install version.
		returns: The version to be installed.
	End Rem
	Method GetVersion:mxModuleVersion()
		Return m_version
	End Method

	Rem
		bbdoc: Set the module to be installed.
		returns: Nothing.
	End Rem
	Method SetModule(modul:mxModule)
		m_module = modul
	End Method

	Rem
		bbdoc: Get the module to be installed.
		returns: The module to be installed.
	End Rem
	Method GetModule:mxModule()
		Return m_module
	End Method

	Rem
		bbdoc: Get the module's scope.
		returns: The module's scope.
	End Rem
	Method GetModuleScope:String()
		Return mxModUtils.GetScopeFromID(m_id)
	End Method

	Rem
		bbdoc: Get the module's name.
		returns: The module's name.
	End Rem
	Method GetModuleName:String()
		Return mxModUtils.GetNameFromID(m_id)
	End Method

	Rem
		bbdoc: Get the versioned-module id.
		returns: The exact module to be installed (e.g. "foo.bar/1.02").
	End Rem
	Method GetVerID:String()
		Return m_id + "/" + m_version.GetName()
	End Method

	Rem
		bbdoc: Get the name of the version to be installed.
		returns: The name of the version to be installed.
	End Rem
	Method GetVersionName:String()
		Return m_version.GetName()
	End Method

'#end region Field accessors

	Rem
		bbdoc: Check the dependency compliance of the current module set.
		returns: Null if all dependencies are matched by existing modules, or a list of module ids that are not yet installed.
	End Rem
	Method CheckCompliance:TListEx()
		Return m_version.GetDependencies().CheckCompliance()
	End Method

	Rem
		bbdoc: Fetch the source archive for the version to be installed.
		returns: Nothing.
	End Rem
	Method FetchSourceArchive()
		m_version.FetchSourceArchive()
	End Method

	Rem
		bbdoc: Unpack the version's archive.
		returns: Nothing.
	End Rem
	Method Unpack()
		Local archivepath:String = m_version.GetTemporaryFilePath()
		Local zreader:ZipReader = New ZipReader
		logger.LogMessage(_s("message.unpacking", [archivepath]))
		If zreader.OpenZip(archivepath)
			Local filename:String, outputpath:String
			Local basepath:String = mainapp.m_modpath + "/" + mxModUtils.GetScopeFromID(m_id) + ".mod/"
			For Local fileinfo:SZipFileEntry = EachIn zreader.m_zipFileList.FileList
				filename = fileinfo.zipFileName
				outputpath = basepath + filename
				'DebugLog("Zip outputpath: ~q" + outputpath + "~q")
				If filename[filename.Length - 1] = 47 ' "/"
					CreateDir(outputpath, True)
				Else
					If CreateFileExplicitly(outputpath)
						zreader.ExtractFileToDisk(filename, outputpath, False)
					Else
						zreader.CloseZip()
						ThrowError(_s("error.writeperms", [outputpath]))
					End If
				End If
			Next
			zreader.CloseZip()

			Local metafile:mxMetaFile = New mxMetaFile.Create(basepath + GetModuleName() + ".mod/meta.maximus")
			metafile.SetMetaData(GetModuleScope(), GetModuleName(), GetVersionName())
			metafile.Save()
		Else
			ThrowError(_s("error.install.openarchive", [archivepath]))
		End If
	End Method

End Type

Rem
	bbdoc: Maximus 'install' argument implementation.
End Rem
Type mxInstallImpl Extends dArgumentImplementation

	Field m_instmap:dObjectMap
	Field m_depcheckmap:dObjectMap
	Field m_unmanagedmap:dObjectMap
	Field m_nobuild:Int = False, m_nounpack:Int = False, m_keeptemp:Int = False
	Field m_nothreaded:Int = False, m_makedocs:Int = False, m_forceinstall:Int = False

	Method New()
		init(["install"])
		m_instmap = New dObjectMap
		m_depcheckmap = New dObjectMap
		m_unmanagedmap = New dObjectMap
	End Method

	Rem
		bbdoc: Check the current arguments for errors (according to the specific implementation).
		returns: Nothing.
		about: This method will throw an error if the arguments are invalid.
	End Rem
	Method CheckArgs()
		If GetArgumentCount() = 0
			ThrowCommonError(mxCmdErrors.REQUIRESPARAMS, m_args.GetName())
		End If
		CheckOptions()
	End Method

	Rem
		bbdoc: Get a string describing the typical usage of the argument.
		returns: A string describing the typical usage of the argument.
	End Rem
	Method GetUsage:String()
		Return _s("arg.install.usage")
	End Method

	Rem
		bbdoc: Execute the implementation's operation.
		returns: Nothing.
	End Rem
	Method Execute()
		m_instmap.Clear()
		m_depcheckmap.Clear()
		m_unmanagedmap.Clear()
		If mainapp.m_sourceshandler
			Local nfounds:dObjectMap = New dObjectMap
			For Local svar:dStringVariable = EachIn m_args
				Local verid:String = svar.Get(), modid:String = mxModUtils.GetIDFromVerID(verid)
				Local modul:mxModule = mainapp.m_sourceshandler.GetModuleWithID(modid)
				If modul
					Local instmod:mxInstModule = New mxInstModule.Create(verid, modul)
					If instmod.SetVersionFromVerID(verid)
						m_instmap._Insert(modid, instmod)
					Else
						ThrowError(_s("arg.install.notfound.version", [modid, mxModUtils.GetVersionFromVerID(verid)]))
					End If
				Else
					nfounds._Insert(modid, modid)
				End If
			Next
			If nfounds.Count() > 0
				logger.LogError(_s("arg.install.notfound.instmods"))
				Local a:String
				For Local b:String = EachIn nfounds.ValueEnumerator()
					a:+ b + " "
				Next
				a = a[..a.Length - 1]
				logger.LogMessage("~t" + a)
				Return
			End If
			If CheckDependencies()
				'Make sure we only install/update managed modules
				If CheckVersions()
					DoInstall()
				End If
			End If
		Else
			ThrowError(_s("error.install.nosources"))
		End If
	End Method

	Rem
		bbdoc: Check the options given to the command.
		returns: Nothing.
	End Rem
	Method CheckOptions()
		m_nobuild = False; m_nounpack = False; m_keeptemp = False
		m_nothreaded = False; m_makedocs = False
		For Local opt:dIdentifier = EachIn m_args
			Select opt.GetName().ToLower()
				Case "-nobuild" m_nobuild = True
				Case "-nounpack" m_nounpack = True
				Case "-keeptemp" m_keeptemp = True
				Case "-nothreaded" m_nothreaded = True
				Case "-makedocs" m_makedocs = True
				Case "-force" m_forceinstall = True
				Default ThrowCommonError(mxOptErrors.UNKNOWN, opt.GetName())
			End Select
		Next
	End Method

	Rem
		bbdoc: Check for version conflictions and warn the user.
		returns: True if the installtion should continue, or False if the user halted the operation.
	End Rem
	Method CheckVersions:Int()
		For Local instmod:mxInstModule = EachIn m_instmap.ValueEnumerator()
			Local ver:String = mxModUtils.GetInstalledVersionFromVerID(instmod.GetVerID())
			'Make sure we skip unmanaged modules
			If ver = "unmanaged"
				m_instmap._Remove(instmod.GetID())
				m_unmanagedmap._Insert(instmod.GetID(), instmod)
			End If
		Next
		Return True
	End Method

	Rem
		bbdoc: Check the dependencies for the modules to be installed.
		returns: Nothing.
	End Rem
	Method CheckDependencies:Int()
		For Local instmod:mxInstModule = EachIn m_instmap.ValueEnumerator()
			CheckModuleDependencies(instmod, nfounds)
		Next
		If nfounds.Count() > 0
			logger.LogError(_s("arg.install.notfound.instdeps"))
			Local a:String
			For Local b:String = EachIn nfounds.ValueEnumerator()
				a:+ b + " "
			Next
			a = a[..a.Length - 1]
			logger.LogMessage("~t" + a)
			If m_forceinstall
				m_nobuild = True
				Local resp:String = Input(_s("arg.install.missingdeps") + " ").ToLower()
				If resp = "y" Or resp = "yes"
					Return True
				End If
			End If
			Return False
		End If
		Return True
	End Method

	Rem
		bbdoc: Check the dependencies for the given module and adapt the install list accordingly.
		returns: Nothing.
	End Rem
	Method CheckModuleDependencies(instmod:mxInstModule, nfounds:dObjectMap)
		Local addlist:TListEx = instmod.CheckCompliance()
		If addlist
			For Local modid:String = EachIn addlist
				If Not m_depcheckmap._Contains(modid)
					Local dmodul:mxModule = mainapp.m_sourceshandler.GetModuleWithID(modid)
					m_depcheckmap._Insert(modid, modid)
					If dmodul
						If Not m_instmap._Contains(modid)
							Local dinstmod:mxInstModule = New mxInstModule.Create(modid, dmodul)
							If instmod.GetVersionName().ToLower() = "dev"
								dinstmod.SetVersionFromName("dev")
							Else
								dinstmod.SetVersion(Null)
							End If
							m_instmap._Insert(modid, dinstmod)
							CheckModuleDependencies(dinstmod, nfounds)
						End If
					Else
						nfounds._Insert(modid, modid)
					End If
				End If
			Next
		End If
	End Method

	Rem
		bbdoc: Do the actual installing.
		returns: Nothing.
	End Rem
	Method DoInstall()
		If m_instmap.Count() > 0
			Local a:String, instmod:mxInstModule

			If m_unmanagedmap.Count()
				logger.LogMessage(_s("arg.install.modulestoskip"))
				For instmod = EachIn m_unmanagedmap.ValueEnumerator()
					a:+instmod.GetVerID() + " "
				Next
				a = a[..a.Length - 1]
				logger.LogMessage("~t" + a)
				a = ""
			End If

			logger.LogMessage(_s("arg.install.modulestoinstall"))
			For instmod = EachIn m_instmap.ValueEnumerator()
				a:+ instmod.GetVerID() + " "
			Next
			a = a[..a.Length - 1]
			logger.LogMessage("~t" + a)
			Local resp:String = Input(_s("arg.install.continuewithinstall") + " ").ToLower()
			If resp = "y" Or resp = "yes"
				For instmod = EachIn m_instmap.ValueEnumerator()
					instmod.FetchSourceArchive()
				Next
				If Not m_nounpack
					For instmod = EachIn m_instmap.ValueEnumerator()
						instmod.Unpack()
					Next
				Else
					logger.LogMessage(_s("message.skipping.unpack"))
				End If
				If Not m_keeptemp Then DeleteDir("tmp/", True)
				If Not m_nobuild
					Local scopes:dObjectMap = New dObjectMap
					For instmod = EachIn m_instmap.ValueEnumerator()
						scopes._Insert(instmod.GetModuleScope(), instmod.GetModuleScope())
					Next
					For Local scope:String = EachIn scopes.ValueEnumerator()
						If mxBMKUtils.MakeMods(scope, m_nothreaded ~ 1) <> 0
							ThrowError(_s("error.install.build", [scope]))
						End If
					Next
					If m_makedocs
						'For instmod = EachIn m_instmap.ValueEnumerator()
						'	mxModUtils.DocMods(instmod.GetID())
						'Next
						' Can't build docs for individual modules yet
						mxModUtils.DocMods()
					End If
				Else
					logger.LogMessage(_s("message.skipping.install"))
				End If
			End If
		Else
			logger.LogMessage(_s("arg.install.nomodulestoinstall"))
			If m_unmanagedmap.Count()
				logger.LogMessage(_s("arg.install.modulestoskip"))
				Local a:String
				For Local instmod:mxInstModule = EachIn m_unmanagedmap.ValueEnumerator()
					a:+instmod.GetVerID() + " "
				Next
				a = a[..a.Length - 1]
				logger.LogMessage("~t" + a)
			End If
		End If
	End Method

End Type

Rem
	bbdoc: Maximus install info module.
	about: This type is used for temporary install information on modules.
End Rem
Type mxInstModule

	Field m_id:String
	Field m_version:mxModuleVersion
	Field m_module:mxModule

	Rem
		bbdoc: Create a new mxInstModule.
		returns: Itself.
	End Rem
	Method Create:mxInstModule(verid:String, modul:mxModule)
		SetID(mxModUtils.GetIDFromVerID(verid))
		SetModule(modul)
		Return Self
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the modid to be installed.
		returns: Nothing.
	End Rem
	Method SetID(id:String)
		m_id = id
	End Method

	Rem
		bbdoc: Get the modid to be installed.
		returns: The modid to be installed.
	End Rem
	Method GetID:String()
		Return m_id
	End Method

	Rem
		bbdoc: Set the version from the given versioned-module name (e.g. "foo.bar/1.02").
		returns: True if the version was set, or False if the version could not be found (was not found).
		about: The latest version will be set if the given id is Null (or if it isn't versioned (e.g. "foo.bar", would result in the latest version being selected)).
	End Rem
	Method SetVersionFromVerID:Int(verid:String)
		Return SetVersionFromName(mxModUtils.GetVersionFromVerID(verid))
	End Method

	Rem
		bbdoc: Set the version to be installed from the given version name (e.g. "1.02").
		returns: True if the version was set, or False if the version could not be set (was not found).
		about: The latest version will be set if the given name is Null.
	End Rem
	Method SetVersionFromName:Int(name:String)
		If Not name
			SetVersion(Null) ' Set the latest version (the version wasn't forced)
			Return True
		Else
			Local ver:mxModuleVersion = m_module.GetVersionWithName(name)
			If ver
				SetVersion(ver)
				Return True
			End If
		End If
		Return False
	End Method

	Rem
		bbdoc: Set the install version.
		returns: Nothing.
		about: If the given value is Null, it will be automatically set to the latest (non-development) version.
	End Rem
	Method SetVersion(version:mxModuleVersion)
		m_version = version
		If Not m_version
			m_version = m_module.GetLatestVersion()
		End If
	End Method

	Rem
		bbdoc: Get the install version.
		returns: The version to be installed.
	End Rem
	Method GetVersion:mxModuleVersion()
		Return m_version
	End Method

	Rem
		bbdoc: Set the module to be installed.
		returns: Nothing.
	End Rem
	Method SetModule(modul:mxModule)
		m_module = modul
	End Method

	Rem
		bbdoc: Get the module to be installed.
		returns: The module to be installed.
	End Rem
	Method GetModule:mxModule()
		Return m_module
	End Method

	Rem
		bbdoc: Get the module's scope.
		returns: The module's scope.
	End Rem
	Method GetModuleScope:String()
		Return mxModUtils.GetScopeFromID(m_id)
	End Method

	Rem
		bbdoc: Get the module's name.
		returns: The module's name.
	End Rem
	Method GetModuleName:String()
		Return mxModUtils.GetNameFromID(m_id)
	End Method

	Rem
		bbdoc: Get the versioned-module id.
		returns: The exact module to be installed (e.g. "foo.bar/1.02").
	End Rem
	Method GetVerID:String()
		Return m_id + "/" + m_version.GetName()
	End Method

	Rem
		bbdoc: Get the name of the version to be installed.
		returns: The name of the version to be installed.
	End Rem
	Method GetVersionName:String()
		Return m_version.GetName()
	End Method

'#end region Field accessors

	Rem
		bbdoc: Check the dependency compliance of the current module set.
		returns: Null if all dependencies are matched by existing modules, or a list of module ids that are not yet installed.
	End Rem
	Method CheckCompliance:TListEx()
		Return m_version.GetDependencies().CheckCompliance()
	End Method

	Rem
		bbdoc: Fetch the source archive for the version to be installed.
		returns: Nothing.
	End Rem
	Method FetchSourceArchive()
		m_version.FetchSourceArchive()
	End Method

	Rem
		bbdoc: Unpack the version's archive.
		returns: Nothing.
	End Rem
	Method Unpack()
		Local archivepath:String = m_version.GetTemporaryFilePath()
		Local zreader:ZipReader = New ZipReader
		logger.LogMessage(_s("message.unpacking", [archivepath]))
		If zreader.OpenZip(archivepath)
			Local filename:String, outputpath:String
			Local basepath:String = mainapp.m_modpath + "/" + mxModUtils.GetScopeFromID(m_id) + ".mod/"
			For Local fileinfo:SZipFileEntry = EachIn zreader.m_zipFileList.FileList
				filename = fileinfo.zipFileName
				outputpath = basepath + filename
				'DebugLog("Zip outputpath: ~q" + outputpath + "~q")
				If filename[filename.Length - 1] = 47 ' "/"
					CreateDir(outputpath, True)
				Else
					If CreateFileExplicitly(outputpath)
						zreader.ExtractFileToDisk(filename, outputpath, False)
					Else
						zreader.CloseZip()
						ThrowError(_s("error.writeperms", [outputpath]))
					End If
				End If
			Next
			zreader.CloseZip()

			Local metafile:mxMetaFile = New mxMetaFile.Create(basepath + GetModuleName() + ".mod/meta.maximus")
			metafile.SetMetaData(GetModuleScope(), GetModuleName(), GetVersionName())
			metafile.Save()
		Else
			ThrowError(_s("error.install.openarchive", [archivepath]))
		End If
	End Method

End Type


*TABSI stricter with replaced ip

*TABSI stricter with replaced ip

' Blue Moon
' FOLD delegate to generate assembly instructions

' this file is a Private Include

'SuperStrict

Type CompileFold
	Field opc:BlueOpcode = New BlueOpcode, bi:BlueInstr = New BlueInstr, f:BlueAssembly, _gq:TList = CreateList()

	Function Make:CompileFold(f:BlueAssembly)
		Local c:CompileFold = New Self ; c.f = f ; Return c
	End Function

	Method l_varref:Node(n:Leaf)
		Local v:VarDef = f.fd.vars[StrToNum(n.val)], op:Int
		If v.isClosure Then op = opc.SETLC Else op = opc.MOV
		Return BytecodeNode.MakeL(n.key, bi.Make(op, v.stkpos, f.Pop()), n)
	End Method
	Method l_closref:Node(n:Leaf)
		Local ref:Int = StrToNum(n.val)
		Return BytecodeNode.MakeL(n.key, bi.Make(opc.SETUPV, ref, f.Pop()))
	End Method
	Method l_getfield:Node(n:Rose)
		Local r:BytecodeNode = BytecodeNode.Join(n.key, [n.arg[0]])
		Local k:Int = f.GetConst("1", Leaf(n.arg[1]).val), tab:Int = f.Pop()
		r.inst.AddLast bi.Make(opc.SETTAB, tab, f.Pop(), k)
		Return r
	End Method
	Method l_index:Node(n:Rose)
		Local r:BytecodeNode = BytecodeNode.Join(n.key, n.arg)
		Local ind:Int = f.Pop(), tab:Int = f.Pop(), val:Int = f.Pop()
		Local pv:BlueInstr = BlueInstr(r.inst.Last())
		If pv.op = opc.LOADSI And Abs(pv.c) < f.VSI_LIMIT And pv.c = Abs pv.c
			pv.op = opc.SETTABSI ; pv.ra = tab ; pv.rb = val
		Else
			r.inst.AddLast bi.Make(opc.SETTABI, tab, val, ind)
		EndIf
		Return r
	End Method
	Method l_discard:Node(n:Node)
		f.Pop() ; Return BytecodeNode.Nil()
	End Method

	Method LocalVar:Node(n:Rose)
		Local vars:Node[] = Rose(BytecodeNode(n.arg[1]).old).arg
		Local r:BytecodeNode = BytecodeNode.Join(n.key, n.arg)
		For Local vi:Int = vars.Length - 1 To 0 Step -1
			Local v:VarDef = f.fd.vars[StrToNum(Leaf(BytecodeNode(vars[vi]).old).val)]
			If v.isClosure Then r.inst.AddFirst bi.Make(opc.NEWUPV, v.stkpos, 0)
		Next
		Return r
	End Method

	Method ReturnS:Node(n:Rose)
		Local r:BytecodeNode = BytecodeNode.Join(n.key, n.arg)
		r.inst.AddLast(bi.MakeK(opc.RET, f.Peek(), n.arg.Length))
		f.Pop(n.arg.Length) ; Return r
	End Method
	Method return_va:Node(n:Node)
		Return BytecodeNode.MakeL(n.key, bi.Make(opc.RETVA, 0, 0))
	End Method

	Method break:Node(n:Node)
		Return BytecodeNode.MakeL(n.key, bi.Make(opc.JMP, -1, -1, -1))
	End Method
	Method RepeatS:Node(n:Rose)
		Local r:BytecodeNode = BytecodeNode.Join(n.key, n.arg), _lt:TList = f.fd._lt, _gt:TList = f.fd._gt
		Local loopStart:LabelDef = New LabelDef, lsID:Int = _lt.Count(), loop:GotoDef = New GotoDef, lID:Int = _gt.Count()
		loop.tgt = loopStart ; _lt.AddLast loopStart ; _gt.AddLast loop
		Local lastop:BlueInstr = BlueInstr(r.inst.Last()), reg:Int = f.Pop()
		If lastop.op = opc.LNOT
			lastop.op = opc.JIF ; lastop.ra = lastop.rb ; lastop.rb = 0 ; lastop.c = lID	'replace NOT with inverted jump
		Else
			r.inst.AddLast bi.MakeK(opc.JNOT, reg, lID)
		EndIf
		r.inst.AddFirst bi.Make(opc.MOV, 0, 0, lsID)
		r.inst.AddLast bi.Make(opc.MOV, -1, -1, -1)
		Return r
	End Method
	Method IfS:Node(n:Rose) { WhileS }
		Local r:BytecodeNode = BytecodeNode.Join(n.key, [n.arg[0]]), lastop:BlueInstr = BlueInstr(r.inst.Last())
		Local before:LabelDef, befID:Int, loopRound:GotoDef, loopID:Int, _lt:TList = f.fd._lt, _gt:TList = f.fd._gt
		If n.key = "WhileS"
			before = New LabelDef ; befID = _lt.Count() ; _lt.AddLast before
			loopRound = New GotoDef ;loopRound.tgt = before ; loopID = _gt.Count() ; _gt.AddLast loopRound
			r.inst.AddFirst bi.Make(opc.MOV, 0, 0, befID)
		EndIf
		Local postThen:LabelDef = New LabelDef, skipThen:GotoDef = New GotoDef, ptID:Int = _lt.Count(), stID:Int = _gt.Count()
		_lt.AddLast postThen ; skipThen.tgt = postThen ; _gt.AddLast skipThen
		If lastop.op = opc.LNOT
			lastop.op = opc.JIF ; lastop.ra = lastop.rb ; lastop.rb = 0 ; lastop.c = stID	'replace NOT with inverted jump
		Else
			r.inst.AddLast bi.MakeK(opc.JNOT, f.Peek(), stID)
		EndIf
		f.Pop()
		For Local op:BlueInstr = EachIn BytecodeNode(n.arg[1]).inst
			r.inst.AddLast op
		Next
		Local postElse:LabelDef, skipElse:GotoDef
		If n.arg.Length > 2	'there's an else
			postElse = New LabelDef ; skipElse = New GotoDef ; skipElse.tgt = postElse
			Local peID:Int = _lt.Count(), seID:Int = _gt.Count()
			_lt.AddLast postElse ; _gt.AddLast skipElse
			r.inst.AddLast bi.MakeK(opc.JMP, 0, seID)
			r.inst.AddLast bi.Make(opc.MOV, 0, 0, ptID)
			For Local op:BlueInstr = EachIn BytecodeNode(n.arg[2]).inst
				r.inst.AddLast op
			Next
			r.inst.AddLast bi.Make(opc.MOV, 0, 0, peID)
		Else
			If n.key = "WhileS" Then r.inst.AddLast bi.MakeK(opc.JMP, 0, loopID) ; r.inst.AddLast bi.Make(opc.MOV, -1, -1, -1)
			r.inst.AddLast bi.Make(opc.MOV, 0, 0, ptID)
		EndIf
		Return r
	End Method
	Method kand:Node(n:Rose) { kor }
		Local r:BytecodeNode = BytecodeNode.Join(n.key, [n.arg[0]]), lastop:BlueInstr = BlueInstr(r.inst.Last())
		Local _lt:TList = f.fd._lt, _gt:TList = f.fd._gt
		Local postR:LabelDef = New LabelDef, skipR:GotoDef = New GotoDef, prID:Int = _lt.Count(), srID:Int = _gt.Count()
		_lt.AddLast postR ; skipR.tgt = postR ; _gt.AddLast skipR
		Local reg:Int = f.Pop()
		If lastop.op = opc.LNOT
			Local op:Int = opc.JIF ; If n.key = "kor" Then op = opc.JNOT
			lastop.op = op ; lastop.ra = lastop.rb ; lastop.rb = 0 ; lastop.c = srID	'replace NOT with inverted jump
		Else
			Local op:Int = opc.JNOT ; If n.key = "kor" Then op = opc.JIF
			r.inst.AddLast bi.MakeK(op, reg - 1, srID)
		EndIf
		For Local op:BlueInstr = EachIn BytecodeNode(n.arg[1]).inst
			r.inst.AddLast op
		Next
		r.inst.AddLast bi.Make(opc.MOV, reg - 1, reg)
		r.inst.AddLast bi.Make(opc.MOV, 0, 0, prID)
		Return r
	End Method

	Method fcall:Node(n:Rose) { pfcall tfcall va_fcall }	'"p" prefix = "procedure" i.e. no return value required
		Local r:BytecodeNode = BytecodeNode.Join(n.key, n.arg), argc:Int = Int(Leaf(n.arg[3]).val), retc:Int = Int(Leaf(n.arg[2]).val)
		Local func:Int = f.Pop(argc + 1), args:Int = func + 1, op:Int = opc.CALL
		If n.key = "tfcall" Then op = opc.TCALL
		r.inst.AddLast bi.Make(op, func, args, argc)
		If n.key = "fcall"
			r.inst.AddLast bi.MakeK(opc.POSTCALL, f.Peek() + 1, retc) ; f.Push(retc)
		ElseIf n.key = "va_fcall"
			r.inst.AddLast bi.Make(opc.CALLINIT, -1, 0)
		EndIf
		Return r
	End Method
	Method mcall:Node(n:Rose) { pmcall tmcall va_mcall }
		Local r:BytecodeNode = BytecodeNode.Join(n.key, n.arg), argc:Int = Int(Leaf(n.arg[3]).val), retc:Int = Int(Leaf(n.arg[2]).val)
		Local func:Int = f.Push(), argv:Int = f.Pop(argc + 2), op:Int = opc.CALL
		Local ks:String = Leaf(Rose(BytecodeNode(n.arg[1]).old).arg[0]).val		'yeesh
		r.inst.AddLast bi.Make(opc.GETTAB, func, argv, f.GetConst("1", ks))
		If n.key = "tmcall" Then op = opc.TCALL
		r.inst.AddLast bi.Make(op, func, argv, argc + 1)
		If n.key = "mcall"
			r.inst.AddLast bi.MakeK(opc.POSTCALL, argv, retc) ; f.Push(retc)
		ElseIf n.key = "va_fcall"
			r.inst.AddLast bi.Make(opc.CALLINIT, -1, 0)
		EndIf
		Return r
	End Method
	Method dotdotdot:Node(n:Leaf)
		Local retc:Int = Int(n.val), r:BytecodeNode
		r = BytecodeNode.MakeL(n.key, bi.MakeK(opc.VARARG, f.Peek() + 1, retc))
		f.Push(retc)
		Return r
	End Method

	Method BinaryOp:Node(n:Rose) { plus minus mul div kmod pow dotdot lt gt leq geq eq neq }
		Local r:BytecodeNode = BytecodeNode.Join(n.key, n.arg), op:Int
		Select n.key
			Case "plus"   ; op = opc.ADD
			Case "minus"  ; op = opc.SUB
			Case "mul"    ; op = opc.MUL
			Case "div"    ; op = opc.DIV
			Case "kmod"   ; op = opc.NMOD
			Case "pow"    ; op = opc.POW
			Case "dotdot" ; op = opc.CAT

			Case "lt"     ; op = opc.LT
			Case "gt"     ; op = opc.LEQ
			Case "leq"    ; op = opc.LEQ
			Case "geq"    ; op = opc.LT
			Case "eq"     ; op = opc.EQ
			Case "neq"    ; op = opc.EQ
		End Select
		Local lop:Int = f.Pop(), rop:Int = f.Pop()
		If n.key = "geq" Or n.key = "gt"
			r.inst.AddLast bi.Make(op, f.Push(), rop, lop)	'technically this violates IEEE-754, but it's how Lua defines it
		Else
			r.inst.AddLast bi.Make(op, f.Push(), lop, rop)
			If n.key = "neq" Then r.inst.AddLast bi.Make(opc.LNOT, f.Peek(), f.Peek())
		EndIf
		Return r
	End Method
	Method UnaryOp:Node(n:Rose) { negate klen knot }
		Local r:BytecodeNode = BytecodeNode.Join(n.key, n.arg), op:Int
		Select n.key
			Case "negate" ; op = opc.UNM
			Case "klen"   ; op = opc.ALEN
			Case "knot"   ; op = opc.LNOT
			Case "posate" ; op = opc.UNP
		End Select
		Local arg:Int = f.Pop()
		r.inst.AddLast bi.Make(op, f.Push(), arg)
		Return r
	End Method

	Method dconst:Node(n:Leaf) { sconst }
		Local ki:Int = Int(n.val), op:Int = opc.LOADK
		If n.key = "dconst" And Abs(ki) < BlueAssembly.SI_LIMIT And Double(n.val) = Double(ki)
			op = opc.LOADSI
		Else
			ki = f.GetConst(String(n.key = "sconst"), n.val)
		EndIf
		Return BytecodeNode.MakeL(n.key, bi.MakeK(op, f.Push(), ki))
	End Method
	Method bconst:Node(n:Leaf) { nil }
		Local ins:BlueInstr
		Select n.val
			Case "true"  ; ins = bi.MakeK(opc.LOADBOOL, f.Push(), 1)
			Case "false" ; ins = bi.MakeK(opc.LOADBOOL, f.Push(), 0)
			Case "nil"   ; ins = bi.MakeK(opc.LOADNIL, f.Push(), 0)
		End Select
		Return BytecodeNode.MakeL(n.key, ins)
	End Method

	Method funref:Node(n:Leaf) { varref closref }
		Local i:BlueInstr, ref:Int = StrToNum(n.val)
		Select n.key
			Case "funref" ; i = bi.MakeK(opc.CLOSURE, f.Push(), ref)
			Case "varref"
				Local v:VarDef = f.fd.vars[ref], op:Int = opc.MOV ; If v.isClosure Then op = opc.GETLC
				i = bi.Make(op, f.Push(), v.stkpos)
			Case "closref" ; i = bi.Make(opc.GETUPV, f.Push(), ref)' + f.fd.vmax)
		End Select
		Return BytecodeNode.MakeL(n.key, i)
	End Method
	Method getfield:Node(n:Rose) { index }
		Local r:BytecodeNode = BytecodeNode.Join(n.key, n.arg), i:BlueInstr
		If n.key = "getfield"
			Local k:Int = f.GetConst("1", Leaf(n.arg[1]).val), tab:Int = f.Pop()
			r.inst.AddLast bi.Make(opc.GETTAB, f.Push(), tab, k)
		Else
			Local idx:Int = f.Pop(), tab:Int = f.Pop()
			Local pv:BlueInstr = BlueInstr(r.inst.Last())
			If pv.op = opc.LOADSI And Abs(pv.c) < f.VSI_LIMIT And pv.c = Abs pv.c
				pv.op = opc.GETTABSI ; pv.ra = f.Push() ; pv.rb = tab
			Else
				r.inst.AddLast bi.Make(opc.GETTABI, f.Push(), tab, idx)
			EndIf
		EndIf
		Return r
	End Method

	Method TableCons:Node(n:Rose)
		Local r:BytecodeNode = BytecodeNode.Join(n.key, n.arg), fld:BytecodeNode = Null
		Local rslot:Int = f.Push(), tslot:Int = f.vars '+ f.upvars
		For fld = EachIn n.arg	'work out where it's safe to put the table
			For Local in:BlueInstr = EachIn fld.inst
				If in.ra >= tslot Then tslot = in.ra + 1
			Next
		Next
		For fld = EachIn n.arg	'then patch the operations to point there
			For Local in:BlueInstr = EachIn fld.inst
				If in.ra = -1 And (in.op = opc.SETTAB Or in.op = opc.SETTABI ..
					Or in.op = opc.VAINIT Or in.op = opc.CALLINIT) Then in.ra = tslot
			Next
		Next
		If n.arg	'pass the ongoing index count to the variable initializer
			fld = BytecodeNode(n.arg[n.arg.Length - 2])
			If fld.key = "va_fcall" Or fld.key = "va_init"
				Local in:BlueInstr = BlueInstr(fld.inst.Last())
				in.c = Int(Leaf(n.arg[n.arg.Length - 1]).val)
			EndIf
		EndIf
		If tslot = -1 Then tslot = rslot Else tslot = f.Push(tslot - f.Peek())
		r.inst.AddFirst bi.Make(opc.NEWTAB, tslot, 0)
		If tslot <> rslot Then r.inst.AddLast bi.Make(opc.MOV, rslot, tslot)	'put the resulting table in the expected slot
		f.Pop(f.Peek() - rslot)	'...and correct the stack
		Return r
	End Method
	Method FieldIndex:Node(n:Rose) { FieldKey }
		Local r:BytecodeNode = BytecodeNode.Join(n.key, n.arg)
		Local val:Int = f.Pop(), idx:Int, op:Int
		If n.key = "FieldIndex"
			idx = f.Pop() ; op = opc.SETTABI
		Else
			idx = f.GetConst("1", Leaf(n.arg[0]).val) ; op = opc.SETTAB
		EndIf
		r.inst.AddLast bi.Make(op, -1, val, idx)
		Return r
	End Method
	Method va_init:Node(n:Node)
		Return BytecodeNode.MakeL(n.key, bi.Make(opc.VAINIT, -1, 0))
	End Method

	Method Label:Node(n:Leaf)	'insert dummy instructions to remove in the next pass
		Return BytecodeNode.MakeL(n.key, bi.Make(opc.MOV, 0, 0, StrToNum(n.val)))
	End Method
	Method GotoS:Node(n:Leaf)	'resolve these in the next pass after removing dummy targets
		Return BytecodeNode.MakeL(n.key, bi.MakeK(opc.JMP, 0, StrToNum(n.val)))
	End Method

	Method PASSTHROUGH:Node(n:Node) { FuncBody Block ExpList FArgs MArgs NameList VarList Assgt }
		If Rose(n)
			Return BytecodeNode.Join(n.key, Rose(n).arg, n)	'combine instruction sequences where they exist
		Else
			Return BytecodeNode.Nil(n.key, n)
		EndIf
	End Method
	Method IGNORE:Node(n:Node) { fname param rcount acount icount }
		Return n	'some things don't represent instructions at all and should be passed as data nodes
	End Method
	Method any:Node(n:Node)
		If Rose(n)
			Print "unhandled (passthrough): " + n.key
			Return BytecodeNode.Join(n.key, Rose(n).arg, n)
		Else
			Print "unhandled: " + n.key
			Return BytecodeNode.Nil(n.key, n)
		EndIf
	End Method
End Type

Type BytecodeNode Extends Node
	Field inst:TList, old:Node
	Function MakeL:BytecodeNode(key:String, ins:BlueInstr, old:Node = Null)
		Local n:BytecodeNode = New Self ; n.key = key ; n.inst = CreateList() ; n.old = old
		n.inst.AddLast(ins) ; Return n
	End Function
	Function MakeR:BytecodeNode(key:String, inst:BlueInstr[], old:Node = Null)
		Local n:BytecodeNode = New Self ; n.key = key ; n.inst = TList.FromArray(inst) ; n.old = old ; Return n
	End Function
	Function Join:BytecodeNode(key:String, els:Node[], old:Node = Null)
		Local n:BytecodeNode = New Self ; n.key = key ; n.inst = CreateList() ; n.old = old
		For Local e:BytecodeNode = EachIn els	'use of Eachin is important, as it autocasts
			For Local o:Object = EachIn e.inst
				n.inst.AddLast o
			Next
		Next
		Return n
	End Function
	Function Nil:BytecodeNode(key:String = "@", old:Node = Null)
		Return MakeR(key, Null, old)
	End Function
End Type

' Blue Moon
' FOLD delegate to generate assembly instructions

' this file is a Private Include

'SuperStrict

Type CompileFold
	Field opc:BlueOpcode = New BlueOpcode, bi:BlueInstr = New BlueInstr, f:BlueAssembly, _gq:TList = CreateList()

	Function Make:CompileFold(f:BlueAssembly)
		Local c:CompileFold = New Self ; c.f = f ; Return c
	End Function

	Method l_varref:Node(n:Leaf)
		Local v:VarDef = f.fd.vars[StrToNum(n.val)], op:Int
		If v.isClosure Then op = opc.SETLC Else op = opc.MOV
		Return BytecodeNode.MakeL(n.key, bi.Make(op, v.stkpos, f.Pop()), n)
	End Method
	Method l_closref:Node(n:Leaf)
		Local ref:Int = StrToNum(n.val)
		Return BytecodeNode.MakeL(n.key, bi.Make(opc.SETUPV, ref, f.Pop()))
	End Method
	Method l_getfield:Node(n:Rose)
		Local r:BytecodeNode = BytecodeNode.Join(n.key, [n.arg[0]])
		Local k:Int = f.GetConst("1", Leaf(n.arg[1]).val), tab:Int = f.Pop()
		r.inst.AddLast bi.Make(opc.SETTAB, tab, f.Pop(), k)
		Return r
	End Method
	Method l_index:Node(n:Rose)
		Local r:BytecodeNode = BytecodeNode.Join(n.key, n.arg)
		Local ind:Int = f.Pop(), tab:Int = f.Pop(), val:Int = f.Pop()
		Local pv:BlueInstr = BlueInstr(r.inst.Last())
		If pv.op = opc.LOADSI
			pv.op = opc.SETTABSI ; pv.ra = tab ; pv.rb = val
		Else
			r.inst.AddLast bi.Make(opc.SETTABI, tab, val, ind)
		EndIf
		Return r
	End Method
	Method l_discard:Node(n:Node)
		f.Pop() ; Return BytecodeNode.Nil()
	End Method

	Method LocalVar:Node(n:Rose)
		Local vars:Node[] = Rose(BytecodeNode(n.arg[1]).old).arg
		Local r:BytecodeNode = BytecodeNode.Join(n.key, n.arg)
		For Local vi:Int = vars.Length - 1 To 0 Step -1
			Local v:VarDef = f.fd.vars[StrToNum(Leaf(BytecodeNode(vars[vi]).old).val)]
			If v.isClosure Then r.inst.AddFirst bi.Make(opc.NEWUPV, v.stkpos, 0)
		Next
		Return r
	End Method

	Method ReturnS:Node(n:Rose)
		Local r:BytecodeNode = BytecodeNode.Join(n.key, n.arg)
		r.inst.AddLast(bi.MakeK(opc.RET, f.Peek(), n.arg.Length))
		f.Pop(n.arg.Length) ; Return r
	End Method
	Method return_va:Node(n:Node)
		Return BytecodeNode.MakeL(n.key, bi.Make(opc.RETVA, 0, 0))
	End Method

	Method break:Node(n:Node)
		Return BytecodeNode.MakeL(n.key, bi.Make(opc.JMP, -1, -1, -1))
	End Method
	Method RepeatS:Node(n:Rose)
		Local r:BytecodeNode = BytecodeNode.Join(n.key, n.arg), _lt:TList = f.fd._lt, _gt:TList = f.fd._gt
		Local loopStart:LabelDef = New LabelDef, lsID:Int = _lt.Count(), loop:GotoDef = New GotoDef, lID:Int = _gt.Count()
		loop.tgt = loopStart ; _lt.AddLast loopStart ; _gt.AddLast loop
		Local lastop:BlueInstr = BlueInstr(r.inst.Last()), reg:Int = f.Pop()
		If lastop.op = opc.LNOT
			lastop.op = opc.JIF ; lastop.ra = lastop.rb ; lastop.rb = 0 ; lastop.c = lID	'replace NOT with inverted jump
		Else
			r.inst.AddLast bi.MakeK(opc.JNOT, reg, lID)
		EndIf
		r.inst.AddFirst bi.Make(opc.MOV, 0, 0, lsID)
		r.inst.AddLast bi.Make(opc.MOV, -1, -1, -1)
		Return r
	End Method
	Method IfS:Node(n:Rose) { WhileS }
		Local r:BytecodeNode = BytecodeNode.Join(n.key, [n.arg[0]]), lastop:BlueInstr = BlueInstr(r.inst.Last())
		Local before:LabelDef, befID:Int, loopRound:GotoDef, loopID:Int, _lt:TList = f.fd._lt, _gt:TList = f.fd._gt
		If n.key = "WhileS"
			before = New LabelDef ; befID = _lt.Count() ; _lt.AddLast before
			loopRound = New GotoDef ;loopRound.tgt = before ; loopID = _gt.Count() ; _gt.AddLast loopRound
			r.inst.AddFirst bi.Make(opc.MOV, 0, 0, befID)
		EndIf
		Local postThen:LabelDef = New LabelDef, skipThen:GotoDef = New GotoDef, ptID:Int = _lt.Count(), stID:Int = _gt.Count()
		_lt.AddLast postThen ; skipThen.tgt = postThen ; _gt.AddLast skipThen
		If lastop.op = opc.LNOT
			lastop.op = opc.JIF ; lastop.ra = lastop.rb ; lastop.rb = 0 ; lastop.c = stID	'replace NOT with inverted jump
		Else
			r.inst.AddLast bi.MakeK(opc.JNOT, f.Peek(), stID)
		EndIf
		f.Pop()
		For Local op:BlueInstr = EachIn BytecodeNode(n.arg[1]).inst
			r.inst.AddLast op
		Next
		Local postElse:LabelDef, skipElse:GotoDef
		If n.arg.Length > 2	'there's an else
			postElse = New LabelDef ; skipElse = New GotoDef ; skipElse.tgt = postElse
			Local peID:Int = _lt.Count(), seID:Int = _gt.Count()
			_lt.AddLast postElse ; _gt.AddLast skipElse
			r.inst.AddLast bi.MakeK(opc.JMP, 0, seID)
			r.inst.AddLast bi.Make(opc.MOV, 0, 0, ptID)
			For Local op:BlueInstr = EachIn BytecodeNode(n.arg[2]).inst
				r.inst.AddLast op
			Next
			r.inst.AddLast bi.Make(opc.MOV, 0, 0, peID)
		Else
			If n.key = "WhileS" Then r.inst.AddLast bi.MakeK(opc.JMP, 0, loopID) ; r.inst.AddLast bi.Make(opc.MOV, -1, -1, -1)
			r.inst.AddLast bi.Make(opc.MOV, 0, 0, ptID)
		EndIf
		Return r
	End Method
	Method kand:Node(n:Rose) { kor }
		Local r:BytecodeNode = BytecodeNode.Join(n.key, [n.arg[0]]), lastop:BlueInstr = BlueInstr(r.inst.Last())
		Local _lt:TList = f.fd._lt, _gt:TList = f.fd._gt
		Local postR:LabelDef = New LabelDef, skipR:GotoDef = New GotoDef, prID:Int = _lt.Count(), srID:Int = _gt.Count()
		_lt.AddLast postR ; skipR.tgt = postR ; _gt.AddLast skipR
		Local reg:Int = f.Pop()
		If lastop.op = opc.LNOT
			Local op:Int = opc.JIF ; If n.key = "kor" Then op = opc.JNOT
			lastop.op = op ; lastop.ra = lastop.rb ; lastop.rb = 0 ; lastop.c = srID	'replace NOT with inverted jump
		Else
			Local op:Int = opc.JNOT ; If n.key = "kor" Then op = opc.JIF
			r.inst.AddLast bi.MakeK(op, reg - 1, srID)
		EndIf
		For Local op:BlueInstr = EachIn BytecodeNode(n.arg[1]).inst
			r.inst.AddLast op
		Next
		r.inst.AddLast bi.Make(opc.MOV, reg - 1, reg)
		r.inst.AddLast bi.Make(opc.MOV, 0, 0, prID)
		Return r
	End Method

	Method fcall:Node(n:Rose) { pfcall tfcall va_fcall }	'"p" prefix = "procedure" i.e. no return value required
		Local r:BytecodeNode = BytecodeNode.Join(n.key, n.arg), argc:Int = Int(Leaf(n.arg[3]).val), retc:Int = Int(Leaf(n.arg[2]).val)
		Local func:Int = f.Pop(argc + 1), args:Int = func + 1, op:Int = opc.CALL
		If n.key = "tfcall" Then op = opc.TCALL
		r.inst.AddLast bi.Make(op, func, args, argc)
		If n.key = "fcall"
			r.inst.AddLast bi.MakeK(opc.POSTCALL, f.Peek() + 1, retc) ; f.Push(retc)
		ElseIf n.key = "va_fcall"
			r.inst.AddLast bi.Make(opc.CALLINIT, -1, 0)
		EndIf
		Return r
	End Method
	Method mcall:Node(n:Rose) { pmcall tmcall va_mcall }
		Local r:BytecodeNode = BytecodeNode.Join(n.key, n.arg), argc:Int = Int(Leaf(n.arg[3]).val), retc:Int = Int(Leaf(n.arg[2]).val)
		Local func:Int = f.Push(), argv:Int = f.Pop(argc + 2), op:Int = opc.CALL
		Local ks:String = Leaf(Rose(BytecodeNode(n.arg[1]).old).arg[0]).val		'yeesh
		r.inst.AddLast bi.Make(opc.GETTAB, func, argv, f.GetConst("1", ks))
		If n.key = "tmcall" Then op = opc.TCALL
		r.inst.AddLast bi.Make(op, func, argv, argc + 1)
		If n.key = "mcall"
			r.inst.AddLast bi.MakeK(opc.POSTCALL, argv, retc) ; f.Push(retc)
		ElseIf n.key = "va_fcall"
			r.inst.AddLast bi.Make(opc.CALLINIT, -1, 0)
		EndIf
		Return r
	End Method
	Method dotdotdot:Node(n:Leaf)
		Local retc:Int = Int(n.val), r:BytecodeNode
		r = BytecodeNode.MakeL(n.key, bi.MakeK(opc.VARARG, f.Peek() + 1, retc))
		f.Push(retc)
		Return r
	End Method

	Method BinaryOp:Node(n:Rose) { plus minus mul div kmod pow dotdot lt gt leq geq eq neq }
		Local r:BytecodeNode = BytecodeNode.Join(n.key, n.arg), op:Int
		Select n.key
			Case "plus"   ; op = opc.ADD
			Case "minus"  ; op = opc.SUB
			Case "mul"    ; op = opc.MUL
			Case "div"    ; op = opc.DIV
			Case "kmod"   ; op = opc.NMOD
			Case "pow"    ; op = opc.POW
			Case "dotdot" ; op = opc.CAT

			Case "lt"     ; op = opc.LT
			Case "gt"     ; op = opc.LEQ
			Case "leq"    ; op = opc.LEQ
			Case "geq"    ; op = opc.LT
			Case "eq"     ; op = opc.EQ
			Case "neq"    ; op = opc.EQ
		End Select
		Local lop:Int = f.Pop(), rop:Int = f.Pop()
		If n.key = "geq" Or n.key = "gt"
			r.inst.AddLast bi.Make(op, f.Push(), rop, lop)	'technically this violates IEEE-754, but it's how Lua defines it
		Else
			r.inst.AddLast bi.Make(op, f.Push(), lop, rop)
			If n.key = "neq" Then r.inst.AddLast bi.Make(opc.LNOT, f.Peek(), f.Peek())
		EndIf
		Return r
	End Method
	Method UnaryOp:Node(n:Rose) { negate klen knot }
		Local r:BytecodeNode = BytecodeNode.Join(n.key, n.arg), op:Int
		Select n.key
			Case "negate" ; op = opc.UNM
			Case "klen"   ; op = opc.ALEN
			Case "knot"   ; op = opc.LNOT
			Case "posate" ; op = opc.UNP
		End Select
		Local arg:Int = f.Pop()
		r.inst.AddLast bi.Make(op, f.Push(), arg)
		Return r
	End Method

	Method dconst:Node(n:Leaf) { sconst }
		Local ki:Int = Int(n.val), op:Int = opc.LOADK
		If n.key = "dconst" And Abs(ki) < BlueAssembly.SI_LIMIT And Double(n.val) = Double(ki)
			op = opc.LOADSI
		Else
			ki = f.GetConst(String(n.key = "sconst"), n.val)
		EndIf
		Return BytecodeNode.MakeL(n.key, bi.MakeK(op, f.Push(), ki))
	End Method
	Method bconst:Node(n:Leaf) { nil }
		Local ins:BlueInstr
		Select n.val
			Case "true"  ; ins = bi.MakeK(opc.LOADBOOL, f.Push(), 1)
			Case "false" ; ins = bi.MakeK(opc.LOADBOOL, f.Push(), 0)
			Case "nil"   ; ins = bi.MakeK(opc.LOADNIL, f.Push(), 0)
		End Select
		Return BytecodeNode.MakeL(n.key, ins)
	End Method

	Method funref:Node(n:Leaf) { varref closref }
		Local i:BlueInstr, ref:Int = StrToNum(n.val)
		Select n.key
			Case "funref" ; i = bi.MakeK(opc.CLOSURE, f.Push(), ref)
			Case "varref"
				Local v:VarDef = f.fd.vars[ref], op:Int = opc.MOV ; If v.isClosure Then op = opc.GETLC
				i = bi.Make(op, f.Push(), v.stkpos)
			Case "closref" ; i = bi.Make(opc.GETUPV, f.Push(), ref)' + f.fd.vmax)
		End Select
		Return BytecodeNode.MakeL(n.key, i)
	End Method
	Method getfield:Node(n:Rose) { index }
		Local r:BytecodeNode = BytecodeNode.Join(n.key, n.arg), i:BlueInstr
		If n.key = "getfield"
			Local k:Int = f.GetConst("1", Leaf(n.arg[1]).val), tab:Int = f.Pop()
			r.inst.AddLast bi.Make(opc.GETTAB, f.Push(), tab, k)
		Else
			Local idx:Int = f.Pop(), tab:Int = f.Pop()
			Local pv:BlueInstr = BlueInstr(r.inst.Last())
			If pv.op = opc.LOADSI And Abs(pv.c) < f.VSI_LIMIT
				pv.op = opc.GETTABSI ; pv.ra = f.Push() ; pv.rb = tab
			Else
				r.inst.AddLast bi.Make(opc.GETTABI, f.Push(), tab, idx)
			EndIf
		EndIf
		Return r
	End Method

	Method TableCons:Node(n:Rose)
		Local r:BytecodeNode = BytecodeNode.Join(n.key, n.arg), fld:BytecodeNode = Null
		Local rslot:Int = f.Push(), tslot:Int = f.vars '+ f.upvars
		For fld = EachIn n.arg	'work out where it's safe to put the table
			For Local in:BlueInstr = EachIn fld.inst
				If in.ra >= tslot Then tslot = in.ra + 1
			Next
		Next
		For fld = EachIn n.arg	'then patch the operations to point there
			For Local in:BlueInstr = EachIn fld.inst
				If in.ra = -1 And (in.op = opc.SETTAB Or in.op = opc.SETTABI ..
					Or in.op = opc.VAINIT Or in.op = opc.CALLINIT) Then in.ra = tslot
			Next
		Next
		If n.arg	'pass the ongoing index count to the variable initializer
			fld = BytecodeNode(n.arg[n.arg.Length - 2])
			If fld.key = "va_fcall" Or fld.key = "va_init"
				Local in:BlueInstr = BlueInstr(fld.inst.Last())
				in.c = Int(Leaf(n.arg[n.arg.Length - 1]).val)
			EndIf
		EndIf
		If tslot = -1 Then tslot = rslot Else tslot = f.Push(tslot - f.Peek())
		r.inst.AddFirst bi.Make(opc.NEWTAB, tslot, 0)
		If tslot <> rslot Then r.inst.AddLast bi.Make(opc.MOV, rslot, tslot)	'put the resulting table in the expected slot
		f.Pop(f.Peek() - rslot)	'...and correct the stack
		Return r
	End Method
	Method FieldIndex:Node(n:Rose) { FieldKey }
		Local r:BytecodeNode = BytecodeNode.Join(n.key, n.arg)
		Local val:Int = f.Pop(), idx:Int, op:Int
		If n.key = "FieldIndex"
			idx = f.Pop() ; op = opc.SETTABI
		Else
			idx = f.GetConst("1", Leaf(n.arg[0]).val) ; op = opc.SETTAB
		EndIf
		r.inst.AddLast bi.Make(op, -1, val, idx)
		Return r
	End Method
	Method va_init:Node(n:Node)
		Return BytecodeNode.MakeL(n.key, bi.Make(opc.VAINIT, -1, 0))
	End Method

	Method Label:Node(n:Leaf)	'insert dummy instructions to remove in the next pass
		Return BytecodeNode.MakeL(n.key, bi.Make(opc.MOV, 0, 0, StrToNum(n.val)))
	End Method
	Method GotoS:Node(n:Leaf)	'resolve these in the next pass after removing dummy targets
		Return BytecodeNode.MakeL(n.key, bi.MakeK(opc.JMP, 0, StrToNum(n.val)))
	End Method

	Method PASSTHROUGH:Node(n:Node) { FuncBody Block ExpList FArgs MArgs NameList VarList Assgt }
		If Rose(n)
			Return BytecodeNode.Join(n.key, Rose(n).arg, n)	'combine instruction sequences where they exist
		Else
			Return BytecodeNode.Nil(n.key, n)
		EndIf
	End Method
	Method IGNORE:Node(n:Node) { fname param rcount acount icount }
		Return n	'some things don't represent instructions at all and should be passed as data nodes
	End Method
	Method any:Node(n:Node)
		If Rose(n)
			Print "unhandled (passthrough): " + n.key
			Return BytecodeNode.Join(n.key, Rose(n).arg, n)
		Else
			Print "unhandled: " + n.key
			Return BytecodeNode.Nil(n.key, n)
		EndIf
	End Method
End Type

Type BytecodeNode Extends Node
	Field inst:TList, old:Node
	Function MakeL:BytecodeNode(key:String, ins:BlueInstr, old:Node = Null)
		Local n:BytecodeNode = New Self ; n.key = key ; n.inst = CreateList() ; n.old = old
		n.inst.AddLast(ins) ; Return n
	End Function
	Function MakeR:BytecodeNode(key:String, inst:BlueInstr[], old:Node = Null)
		Local n:BytecodeNode = New Self ; n.key = key ; n.inst = TList.FromArray(inst) ; n.old = old ; Return n
	End Function
	Function Join:BytecodeNode(key:String, els:Node[], old:Node = Null)
		Local n:BytecodeNode = New Self ; n.key = key ; n.inst = CreateList() ; n.old = old
		For Local e:BytecodeNode = EachIn els	'use of Eachin is important, as it autocasts
			For Local o:Object = EachIn e.inst
				n.inst.AddLast o
			Next
		Next
		Return n
	End Function
	Function Nil:BytecodeNode(key:String = "@", old:Node = Null)
		Return MakeR(key, Null, old)
	End Function
End Type


Fixed wireframe viewing in tron sample.

Fixed wireframe viewing in tron sample.

Strict

Import MaxB3D.Drivers

Graphics 800,600

Local smooth=True

Local grid_tex:TTexture=CreateTexture( 32,32,8 )
SetTextureScale grid_tex,10,10
SetBuffer TextureBuffer( grid_tex )
SetColor 0,0,64;DrawRect 0,0,32,32
SetColor 0,0,255;DrawRect 0,0,32,32
SetBuffer BackBuffer()

Local grid_plane:TFlat=CreateFlat()
SetEntityTexture grid_plane,grid_tex
SetEntityBlend grid_plane,1
SetEntityAlpha grid_plane,.6
SetEntityFX grid_plane,FX_FULLBRIGHT

'Local mirror:TMirror=CreateMirror()
rem
Local pivot=CreatePivot()

p=CreatePivot( p )
cube=CreateCube( p )
ScaleEntity cube,1,1,5
SetAnimKey cube,0
RotateEntity cube,0,120,0
SetAnimKey cube,60
RotateEntity cube,0,240,0
SetAnimKey cube,120
RotateEntity cube,0,0,0
SetAnimKey cube,180
AddAnimSeq p,180

For x=-100 To 100 Step 25
For z=-100 To 100 Step 25
e=CopyEntity( p,pivot )
PositionEntity e,x,5,z
Animate e
Next
Next
FreeEntity cube
End Rem

Local trail_mesh:TMesh=CreateMesh()
Local trail_brush:TBrush=CreateBrush()
SetBrushColor trail_brush,255,0,0
SetBrushBlend trail_brush,3
SetBrushFX trail_brush,FX_FULLBRIGHT
Local trail_surf:TSurface=AddMeshSurface( trail_mesh,trail_brush )
AddSurfaceVertex trail_surf,0,2,0,0,0
AddSurfaceVertex trail_surf,0,0,0,0,1
AddSurfaceVertex trail_surf,0,2,0,0,0
AddSurfaceVertex trail_surf,0,0,0,0,1
AddSurfaceTriangle trail_surf,0,2,3
AddSurfaceTriangle trail_surf,0,3,1
AddSurfaceTriangle trail_surf,0,3,2
AddSurfaceTriangle trail_surf,0,1,3
Local trail_vert=2

Local bike:TMesh=CreateSphere()
ScaleMesh bike,.75,1,2
SetEntityPosition bike,0,1,0
SetEntityShine bike,1
SetEntityColor bike,192,0,255

Local camera:TCamera=CreateCamera()
TurnEntity camera,45,0,0
Local cam_d#=30

Local light:TLight=CreateLight()
TurnEntity light,45,45,0

Local add_flag=False,add_cnt

While Not KeyHit(KEY_ESCAPE) And Not AppTerminate()
	SetWireFrame KeyDown(KEY_W)

	If KeyDown(KEY_A) cam_d=cam_d-1
	If KeyDown(KEY_Z) cam_d=cam_d+1

	Local turn=0
	If smooth
		If KeyDown(KEY_LEFT) turn=5
		If KeyDown(KEY_RIGHT) turn=-5
		If turn
			add_cnt=add_cnt+1
			If add_cnt=3
				add_cnt=0
				add_flag=True
			Else
				add_flag=False
			EndIf
		Else If add_cnt
			add_cnt=0
			add_flag=True
		Else
			add_flag=False
		EndIf
	Else
		If KeyHit(KEY_LEFT) turn=90
		If KeyHit(KEY_RIGHT) turn=-90
		If turn Then add_flag=True Else add_flag=False
	EndIf

	If turn
		TurnEntity bike,0,turn,0
	EndIf

	MoveEntity bike,0,0,1

	Local x#,y#,z#
	GetEntityPosition bike,x,y,z
	If add_flag
		AddSurfaceVertex trail_surf,x,2,z,0,0
		AddSurfaceVertex trail_surf,x,0,z,0,1
		AddSurfaceTriangle trail_surf,trail_vert,trail_vert+2,trail_vert+3
		AddSurfaceTriangle trail_surf,trail_vert,trail_vert+3,trail_vert+1
		AddSurfaceTriangle trail_surf,trail_vert,trail_vert+3,trail_vert+2
		AddSurfaceTriangle trail_surf,trail_vert,trail_vert+1,trail_vert+3
		trail_vert=trail_vert+2
	Else
		SetSurfaceCoords trail_surf,trail_vert,x,2,z
		SetSurfaceCoords trail_surf,trail_vert+1,x,0,z
	EndIf

	UpdateWorld

	SetEntityPosition camera,x-5,0,z
	MoveEntity camera,0,0,-cam_d

'	PositionEntity camera,0,20,0
'	PointEntity camera,bike

	RenderWorld
	Flip
Wend

Strict

Import MaxB3D.Drivers

Graphics 800,600

Local smooth=True

Local grid_tex:TTexture=CreateTexture( 32,32,8 )
SetTextureScale grid_tex,10,10
SetBuffer TextureBuffer( grid_tex )
SetColor 0,0,64;DrawRect 0,0,32,32
SetColor 0,0,255;DrawRect 0,0,32,32
SetBuffer BackBuffer()

Local grid_plane:TFlat=CreateFlat()
SetEntityTexture grid_plane,grid_tex
SetEntityBlend grid_plane,1
SetEntityAlpha grid_plane,.6
SetEntityFX grid_plane,FX_FULLBRIGHT

'Local mirror:TMirror=CreateMirror()
rem
Local pivot=CreatePivot()

p=CreatePivot( p )
cube=CreateCube( p )
ScaleEntity cube,1,1,5
SetAnimKey cube,0
RotateEntity cube,0,120,0
SetAnimKey cube,60
RotateEntity cube,0,240,0
SetAnimKey cube,120
RotateEntity cube,0,0,0
SetAnimKey cube,180
AddAnimSeq p,180

For x=-100 To 100 Step 25
For z=-100 To 100 Step 25
e=CopyEntity( p,pivot )
PositionEntity e,x,5,z
Animate e
Next
Next
FreeEntity cube
End Rem

Local trail_mesh:TMesh=CreateMesh()
Local trail_brush:TBrush=CreateBrush()
SetBrushColor trail_brush,255,0,0
SetBrushBlend trail_brush,3
SetBrushFX trail_brush,FX_FULLBRIGHT
Local trail_surf:TSurface=AddMeshSurface( trail_mesh,trail_brush )
AddSurfaceVertex trail_surf,0,2,0,0,0
AddSurfaceVertex trail_surf,0,0,0,0,1
AddSurfaceVertex trail_surf,0,2,0,0,0
AddSurfaceVertex trail_surf,0,0,0,0,1
AddSurfaceTriangle trail_surf,0,2,3
AddSurfaceTriangle trail_surf,0,3,1
AddSurfaceTriangle trail_surf,0,3,2
AddSurfaceTriangle trail_surf,0,1,3
Local trail_vert=2

Local bike:TMesh=CreateSphere()
ScaleMesh bike,.75,1,2
SetEntityPosition bike,0,1,0
SetEntityShine bike,1
SetEntityColor bike,192,0,255

Local camera:TCamera=CreateCamera()
TurnEntity camera,45,0,0
Local cam_d#=30

Local light:TLight=CreateLight()
TurnEntity light,45,45,0

Local add_flag=False,add_cnt

While Not KeyHit(KEY_ESCAPE) And Not AppTerminate()
	SetWireFrame KeyHit(KEY_W)

	If KeyDown(KEY_A) cam_d=cam_d-1
	If KeyDown(KEY_Z) cam_d=cam_d+1

	Local turn=0
	If smooth
		If KeyDown(KEY_LEFT) turn=5
		If KeyDown(KEY_RIGHT) turn=-5
		If turn
			add_cnt=add_cnt+1
			If add_cnt=3
				add_cnt=0
				add_flag=True
			Else
				add_flag=False
			EndIf
		Else If add_cnt
			add_cnt=0
			add_flag=True
		Else
			add_flag=False
		EndIf
	Else
		If KeyHit(KEY_LEFT) turn=90
		If KeyHit(KEY_RIGHT) turn=-90
		If turn Then add_flag=True Else add_flag=False
	EndIf

	If turn
		TurnEntity bike,0,turn,0
	EndIf

	MoveEntity bike,0,0,1

	Local x#,y#,z#
	GetEntityPosition bike,x,y,z
	If add_flag
		AddSurfaceVertex trail_surf,x,2,z,0,0
		AddSurfaceVertex trail_surf,x,0,z,0,1
		AddSurfaceTriangle trail_surf,trail_vert,trail_vert+2,trail_vert+3
		AddSurfaceTriangle trail_surf,trail_vert,trail_vert+3,trail_vert+1
		AddSurfaceTriangle trail_surf,trail_vert,trail_vert+3,trail_vert+2
		AddSurfaceTriangle trail_surf,trail_vert,trail_vert+1,trail_vert+3
		trail_vert=trail_vert+2
	Else
		SetSurfaceCoords trail_surf,trail_vert,x,2,z
		SetSurfaceCoords trail_surf,trail_vert+1,x,0,z
	EndIf

	UpdateWorld

	SetEntityPosition camera,x-5,0,z
	MoveEntity camera,0,0,-cam_d

'	PositionEntity camera,0,20,0
'	PointEntity camera,bike

	RenderWorld
	Flip
Wend