Forgot to rename option

Forgot to rename option

refs gh-18

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
	Field m_overwriteunmanaged: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
				Case "-overwriteunmanaged" m_overwriteunmanaged = 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"
				If Not m_overwriteunmanaged Then 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()
				If m_overwriteunmanaged
					logger.LogMessage(_s("arg.install.modulestooverwrite"))
				Else
					logger.LogMessage(_s("arg.install.modulestoskip"))
				End If
				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/"
			DeleteDir(mxModUtils.ModulePath(m_id), True)
			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
	Field m_overwriteunmanaged: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
				Case "-ignoreunmanaged" m_overwriteunmanaged = 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"
				If Not m_overwriteunmanaged Then 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()
				If m_overwriteunmanaged
					logger.LogMessage(_s("arg.install.modulestooverwrite"))
				Else
					logger.LogMessage(_s("arg.install.modulestoskip"))
				End If
				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/"
			DeleteDir(mxModUtils.ModulePath(m_id), True)
			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


Removed experimental D3D11 driver from default imports.

Strict

Rem
	bbdoc: MaxB3D driver collection
End Rem
Module MaxB3D.Drivers
ModuleInfo "Author: Kevin Primm"
ModuleInfo "License: MIT"

Import MaxB3D.Core
Import MaxB3D.B3DCollision
Import MaxB3D.Primitives
Import MaxB3D.GLDriver
Import MaxB3D.D3D9Driver
Import MaxB3D.Functions

Strict

Rem
	bbdoc: MaxB3D driver collection
End Rem
Module MaxB3D.Drivers
ModuleInfo "Author: Kevin Primm"
ModuleInfo "License: MIT"

Import MaxB3D.Core
Import MaxB3D.B3DCollision
Import MaxB3D.Primitives
Import MaxB3D.GLDriver
Import MaxB3D.D3D9Driver
Import MaxB3D.D3D11Driver
Import MaxB3D.Functions


Added RuntimeError if developer tries to load a non-valid sound format into MonkeyMax

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

Global app:gxtkApp

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

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

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

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

		bb_input_SetInputDevice(ginput)
		bb_audio_SetAudioDevice(gaudio)

		startMillis=BlitzMaxMillisecs()

		SetFrameRate( 0 )

		InvokeOnCreate()
		InvokeOnRender()
		Update()
	EndMethod

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

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

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

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

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

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

	Method LoadState:String()
'		var file:SharedObject=SharedObject.getLocal( "gxtkapp" );
'		var state:String=file.data.state;
'		file.close();
'		if( state ) return state;
		Return ""
	EndMethod

	Method SaveState:Int( state:String )
'		var file:SharedObject=SharedObject.getLocal( "gxtkapp" );
'		file.data.state=state;
'		file.close();
		Return 0
	EndMethod

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

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

	Method GraphicsDevice:gxtkGraphics()
		Return ggraphics
	EndMethod

	Method InputDevice:gxtkInput()
		Return ginput
	EndMethod

	Method AudioDevice:gxtkAudio()
		Return gaudio
	EndMethod

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

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

	Method Loading:Int()
		Return vloading
	EndMethod

	Method OnCreate:Int()
		Return 0
	EndMethod

	Method OnUpdate:Int()
		Return 0
	EndMethod

	Method OnSuspend:Int()
		Return 0
	EndMethod

	Method OnResume:Int()
		Return 0
	EndMethod

	Method OnRender:Int()
		Return 0
	EndMethod

	Method OnLoading:Int()
		Return 0
	EndMethod
EndType

Type gxtkGraphics
	Field gmode:Int = 1

	Method Mode:Int()
		Return gmode
	EndMethod

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

	Method DrawPoint:Int(x:Float, y:Float)
		Plot x, y
	EndMethod

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

	Method BeginRender()
	EndMethod

	Method EndRender()
		Flip
	EndMethod

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

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

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

	Method Width:Int()
		Return GraphicsWidth()
	EndMethod

	Method Height:Int()
		Return GraphicsHeight()
	EndMethod

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

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

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

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

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

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

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

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

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

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

	Method KeyDown:Int( key:Int )
		if( key = 384 ) key = 1 ' change TOUCH0 to MOUSE_LMB

		If( key >= 1 And key <= 3 )
			Return BRL.PolledInput.MouseDown( key )
		EndIf
		If key < 256
			Return BRL.PolledInput.KeyDown( key )
		Else
			Return 0
		EndIf
	EndMethod

	Method KeyHit:Int( key:Int )
		if( key = 384 ) key = 1 ' change TOUCH0 to MOUSE_LMB
		If( key >= 1 And key <= 3 )
			Return BRL.PolledInput.MouseHit( key )
		EndIf
		If key < 256
			Return BRL.PolledInput.KeyHit( key )
		Else
			Return 0
		EndIf
	EndMethod

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

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

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

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

	Method TouchX:Float( index:Int )
		Return BRL.PolledInput.MouseX()
	EndMethod

	Method TouchY:Float( index:Int )
		Return BRL.PolledInput.MouseY()
	EndMethod

	Method AccelX:Int()
		return 0
	EndMethod

	Method AccelY:Int()
		return 0
	EndMethod

	Method AccelZ:Int()
		return 0
	EndMethod

	Method SetKeyboardEnabled:Int( enabled:int )
		Return 0
	EndMethod
EndType

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

	Method New()
		channel = New TChannel
	EndMethod
EndType

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

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

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

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

	Method ChannelState:int( channel:int )
		Return -1
	EndMethod

	Method StopMusic()
	EndMethod

	Method StopChannel( channel:Int )
	EndMethod

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

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

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

		PlaySound( sound.sound, chan.channel )
	EndMethod

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

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

	Method PauseMusic:Int()
		Return 0
	EndMethod

	Method ResumeMusic:Int()
		Return 0
	EndMethod

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

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

	Method LoadSample:gxtkSample( path:String )
		Local extension:String = ExtractExt( path)
		If extension = "ogg" Or extension = "wav" Then
			Local sound:TSound = LoadSound("data/"+path)
			If sound Then
				Local gs:gxtkSample = New gxtkSample
				gs.setSound(sound)
				Return gs
			EndIf
		Else
			RuntimeError "BlitzMax can only use ogg and wav file formats"
		EndIf
		Return Null
	EndMethod
EndType

Type gxtkSample
	Field sound:TSound

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

	Method Discard()
		Self.sound = null
	EndMethod
EndType

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

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

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

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

Function BlitzMaxMillisecs:Int()
	Return MilliSecs()
EndFunction

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

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

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

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

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

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

	Method Width:Int()
		Return image.Width
	EndMethod

	Method Height:Int()
		Return image.Height
	EndMethod

	Method Discard()
	EndMethod
EndType

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

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

Global app:gxtkApp

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

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

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

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

		bb_input_SetInputDevice(ginput)
		bb_audio_SetAudioDevice(gaudio)

		startMillis=BlitzMaxMillisecs()

		SetFrameRate( 0 )

		InvokeOnCreate()
		InvokeOnRender()
		Update()
	EndMethod

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

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

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

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

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

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

	Method LoadState:String()
'		var file:SharedObject=SharedObject.getLocal( "gxtkapp" );
'		var state:String=file.data.state;
'		file.close();
'		if( state ) return state;
		Return ""
	EndMethod

	Method SaveState:Int( state:String )
'		var file:SharedObject=SharedObject.getLocal( "gxtkapp" );
'		file.data.state=state;
'		file.close();
		Return 0
	EndMethod

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

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

	Method GraphicsDevice:gxtkGraphics()
		Return ggraphics
	EndMethod

	Method InputDevice:gxtkInput()
		Return ginput
	EndMethod

	Method AudioDevice:gxtkAudio()
		Return gaudio
	EndMethod

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

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

	Method Loading:Int()
		Return vloading
	EndMethod

	Method OnCreate:Int()
		Return 0
	EndMethod

	Method OnUpdate:Int()
		Return 0
	EndMethod

	Method OnSuspend:Int()
		Return 0
	EndMethod

	Method OnResume:Int()
		Return 0
	EndMethod

	Method OnRender:Int()
		Return 0
	EndMethod

	Method OnLoading:Int()
		Return 0
	EndMethod
EndType

Type gxtkGraphics
	Field gmode:Int = 1

	Method Mode:Int()
		Return gmode
	EndMethod

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

	Method DrawPoint:Int(x:Float, y:Float)
		Plot x, y
	EndMethod

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

	Method BeginRender()
	EndMethod

	Method EndRender()
		Flip
	EndMethod

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

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

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

	Method Width:Int()
		Return GraphicsWidth()
	EndMethod

	Method Height:Int()
		Return GraphicsHeight()
	EndMethod

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

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

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

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

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

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

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

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

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

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

	Method KeyDown:Int( key:Int )
		if( key = 384 ) key = 1 ' change TOUCH0 to MOUSE_LMB

		If( key >= 1 And key <= 3 )
			Return BRL.PolledInput.MouseDown( key )
		EndIf
		If key < 256
			Return BRL.PolledInput.KeyDown( key )
		Else
			Return 0
		EndIf
	EndMethod

	Method KeyHit:Int( key:Int )
		if( key = 384 ) key = 1 ' change TOUCH0 to MOUSE_LMB
		If( key >= 1 And key <= 3 )
			Return BRL.PolledInput.MouseHit( key )
		EndIf
		If key < 256
			Return BRL.PolledInput.KeyHit( key )
		Else
			Return 0
		EndIf
	EndMethod

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

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

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

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

	Method TouchX:Float( index:Int )
		Return BRL.PolledInput.MouseX()
	EndMethod

	Method TouchY:Float( index:Int )
		Return BRL.PolledInput.MouseY()
	EndMethod

	Method AccelX:Int()
		return 0
	EndMethod

	Method AccelY:Int()
		return 0
	EndMethod

	Method AccelZ:Int()
		return 0
	EndMethod

	Method SetKeyboardEnabled:Int( enabled:int )
		Return 0
	EndMethod
EndType

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

	Method New()
		channel = New TChannel
	EndMethod
EndType

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

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

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

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

	Method ChannelState:int( channel:int )
		Return -1
	EndMethod

	Method StopMusic()
	EndMethod

	Method StopChannel( channel:Int )
	EndMethod

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

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

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

		PlaySound( sound.sound, chan.channel )
	EndMethod

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

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

	Method PauseMusic:Int()
		Return 0
	EndMethod

	Method ResumeMusic:Int()
		Return 0
	EndMethod

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

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

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

Type gxtkSample
	Field sound:TSound

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

	Method Discard()
		Self.sound = null
	EndMethod
EndType

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

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

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

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

Function BlitzMaxMillisecs:Int()
	Return MilliSecs()
EndFunction

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

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

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

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

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

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

	Method Width:Int()
		Return image.Width
	EndMethod

	Method Height:Int()
		Return image.Height
	EndMethod

	Method Discard()
	EndMethod
EndType

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