// draws unreal-style main menu
Class UnrealListMenu : ListMenu
{
	TextureID tex[3];

	override void Init( Menu parent, ListMenuDescriptor desc )
	{
		Super.Init(parent,desc);
		tex[0] = TexMan.CheckForTexture("graphics/rmetal.png",TexMan.Type_Any);
		tex[1] = TexMan.CheckForTexture("graphics/menubarr.png",TexMan.Type_Any);
		tex[2] = TexMan.CheckForTexture("graphics/unlogo.png",TexMan.Type_Any);
	}
	override void Drawer()
	{
		double StartX = 0.5*CleanWidth_1-128;
		int num = (CleanHeight_1/512)+1;
		for ( int i=0; i<=num; i++ )
			Screen.DrawTexture(tex[0],false,StartX*CleanXFac_1,512*CleanYFac_1*i,DTA_CleanNoMove_1,true);
		Screen.DrawTexture(tex[1],false,StartX*CleanXFac_1,(CleanHeight_1-58)*CleanYFac_1,DTA_CleanNoMove_1,true,DTA_LegacyRenderStyle,STYLE_Add);
		Screen.DrawTexture(tex[2],false,StartX*CleanXFac_1,(CleanHeight_1-52)*CleanYFac_1,DTA_CleanNoMove_1,true);
		Super.Drawer();
	}
	override bool MouseEvent( int type, int x, int y )
	{
		int sel = -1;
		if ( mFocusControl )
		{
			x = ((x-(screen.GetWidth()/2))/CleanXfac)+160;
			y = ((y-(screen.GetHeight()/2))/CleanYfac)+100;
			mFocusControl.MouseEvent(type,x,y);
			return true;
		}
		else
		{
			// menu uses different scaling
			x /= CleanXfac_1;
			y /= CleanYfac_1;
			for ( int i=0; i<mDesc.mItems.Size(); i++ )
			{
				if ( mDesc.mItems[i].CheckCoordinate(x,y) )
				{
					mDesc.mSelectedItem = i;
					mDesc.mItems[i].MouseEvent(type,x,y);
					return true;
				}
			}
		}
		mDesc.mSelectedItem = -1;
		return true;
	}
}

Class ListMenuItemUnrealTextItem : ListMenuItemSelectable
{
	String mText;
	Font mFont;
	int mColor;
	int mSpacing;

	void Init( ListMenuDescriptor desc, String text, String hotkey, Name child, int param = 0 )
	{
		Super.Init(desc.mXpos,desc.mYpos,desc.mLinespacing,child,param);
		mText = text;
		mFont = Font.GetFont('ULargeFont');
		mColor = Font.CR_UNTRANSLATED;
		mSpacing = desc.mLineSpacing;
		mHotkey = hotkey.GetNextCodePoint(0);
	}

	void InitDirect( double x, double y, int height, String hotkey, String text, Font font, int color, int color2, Name child, int param = 0 )
	{
		Super.Init(x,y,height,child,param);
		mText = text;
		mFont = Font.GetFont('ULargeFont');
		mColor = Font.CR_UNTRANSLATED;
		int pos = 0;
		mHotkey = hotkey.GetNextCodePoint(0);
	}

	override void Drawer( bool selected )
	{
		String str = StringTable.Localize(mText);
		let fnt = (generic_ui||!mFont.CanPrint(str))?NewSmallFont:mFont;
		double basex = floor(0.5*(CleanWidth_1-fnt.StringWidth(str)));
		double basey = floor(0.25*(CleanHeight_1-mSpacing*5));
		Screen.DrawText(fnt,(fnt==NewSmallFont)?Font.CR_GREEN:mColor,(basex+mXPos)*CleanXFac_1,(basey+mYpos)*CleanYFac_1,str,DTA_CleanNoMove_1,true,DTA_Alpha,selected?1.0:0.5);
	}

	override int GetWidth()
	{
		String str = StringTable.Localize(mText);
		let fnt = (generic_ui||!mFont.CanPrint(str))?NewSmallFont:mFont;
		return max(1,fnt.StringWidth(StringTable.Localize(mText)));
	}

	override bool CheckCoordinate( int x, int y )
	{
		String str = StringTable.Localize(mText);
		let fnt = (generic_ui||!mFont.CanPrint(str))?NewSmallFont:mFont;
		int w = fnt.StringWidth(str);
		int h = fnt.GetHeight();
		double basex = floor(0.5*(CleanWidth_1-w))+mXPos;
		double basey = floor(0.25*(CleanHeight_1-mSpacing*5))+mYPos;
		return (mEnabled && (x >= basex) && (x < basex+w) && (y >= basey) && (y < basey+h));
	}

	override void DrawSelector( double xofs, double yofs, TextureID tex )
	{
		// nothing
	}
}

// for hud config
Class OptionMenuItemHudType : OptionMenuItem
{
	TextureID tex[6];
	CVar mCVar;

	OptionMenuItemHudType Init( String label )
	{
		Super.Init(label,"",true);
		mCVar = CVar.FindCVar('sting_hudmode');
		tex[0] = TexMan.CheckForTexture("graphics/Hud1.png",TexMan.Type_Any);
		tex[1] = TexMan.CheckForTexture("graphics/Hud2.png",TexMan.Type_Any);
		tex[2] = TexMan.CheckForTexture("graphics/Hud3.png",TexMan.Type_Any);
		tex[3] = TexMan.CheckForTexture("graphics/Hud4.png",TexMan.Type_Any);
		tex[4] = TexMan.CheckForTexture("graphics/Hud5.png",TexMan.Type_Any);
		tex[5] = TexMan.CheckForTexture("graphics/Hud6.png",TexMan.Type_Any);
		return self;
	}

	override bool Selectable()
	{
		return false;
	}

	override int Draw( OptionMenuDescriptor desc, int y, int indent, bool selected )
	{
		int xpos = indent + CursorSpace();
		int ypos = y + OptionMenuSettings.mLinespacing*CleanYfac_1;
		Screen.DrawFrame(xpos,ypos,64*CleanXFac_1,64*CleanYFac_1);
		Screen.DrawTexture(tex[mCVar.GetInt()],false,xpos,ypos,DTA_CleanNoMove_1,true);
		return -1;
	}
}
Class OptionMenuItemUFacePreview : OptionMenuItem
{
	String texpath[5];
	TextureID talkface[5];
	CVar mCVar[5];

	OptionMenuItemUFacePreview Init( String dummy )
	{
		Super.Init(dummy,'None',true);
		mCVar[0] = CVar.FindCVar('sting_female1skin');
		mCVar[1] = CVar.FindCVar('sting_female2skin');
		mCVar[2] = CVar.FindCVar('sting_male1skin');
		mCVar[3] = CVar.FindCVar('sting_male2skin');
		mCVar[4] = CVar.FindCVar('sting_male3skin');
		return self;
	}

	override int Draw( OptionMenuDescriptor desc, int y, int indent, bool selected )
	{
		int x = Screen.GetWidth()/2-176*CleanXFac_1;
		for ( int i=0; i<5; i++ )
		{
			String path;
			switch ( i )
			{
			default:
			case 0:
				path = "models/Female1/Face"..mCVar[0].GetString()..".png";
				break;
			case 1:
				path = "models/Female2/Face"..mCVar[1].GetString()..".png";
				break;
			case 2:
				path = "models/Male1/Face"..mCVar[2].GetString()..".png";
				break;
			case 3:
				path = "models/Male2/Face"..mCVar[3].GetString()..".png";
				break;
			case 4:
				path = "models/Male3/Face"..mCVar[4].GetString()..".png";
				break;
			}
			if ( (texpath[i] != path) || talkface[i].IsNull() )
			{
				texpath[i] = path;
				talkface[i] = TexMan.CheckForTexture(path,TexMan.Type_Any);
			}
			Screen.DrawTexture(talkface[i],false,x,y-8*CleanYFac_1,DTA_CleanNoMove_1,true);
			x += 72*CleanXFac_1;
		}
		return -1;
	}

	override bool Selectable()
	{
		return false;
	}
}

// because I can't change the font or color in mapinfo
Class GreenMessageBox : MessageBoxMenu
{
	override void Init( Menu parent, String message, int messagemode, bool playsound, Name cmd, voidptr native_handler )
	{
		Super.Init(parent,message,messagemode,playsound,cmd,native_handler);
		Font NFont = Font.GetFont('UMedFont');
		if ( !generic_ui )
		{
			if ( NFont && NFont.CanPrint(message) && NFont.CanPrint("$TXT_YES") && NFont.CanPrint("$TXT_NO") ) textFont = NFont;
			else if ( OriginalSmallFont && OriginalSmallFont.CanPrint(message) && OriginalSmallFont.CanPrint("$TXT_YES") && OriginalSmallFont.CanPrint("$TXT_NO") ) textFont = OriginalSmallFont;
		}
		if ( !textFont )
		{
			arrowFont = textFont = NewSmallFont;
			int factor = (CleanXfac+1)/2;
			destWidth = screen.GetWidth()/factor;
			destHeight = screen.GetHeight()/factor;
			selector = "▶";
		}
		else
		{
			arrowFont = ConFont;
			destWidth = CleanWidth;
			destHeight = CleanHeight;
			selector = "\xd";
		}
		int mr1 = destWidth/2+10+textFont.StringWidth(Stringtable.Localize("$TXT_YES"));
		int mr2 = destWidth/2+10+textFont.StringWidth(Stringtable.Localize("$TXT_NO"));
		mMouseRight = MAX(mr1,mr2);
		mMessage = textFont.BreakLines(Stringtable.Localize(message),generic_ui?600:300);
	}
	override void Drawer ()
	{
		int i, y;
		int fontheight = textFont.GetHeight();
		y = destHeight/2;
		int c = mMessage.Count();
		y -= c*fontHeight/2;
		for ( i=0; i<c; i++ )
		{
			screen.DrawText (textFont,Font.CR_GREEN,(destWidth/2)-mMessage.StringWidth(i)/2,y,mMessage.StringAt(i),DTA_VirtualWidth,destWidth,DTA_VirtualHeight,destHeight,DTA_KeepRatio,true);
			y += fontheight;
		}
		if ( mMessageMode == 0 )
		{
			y += fontheight;
			mMouseY = y;
			screen.DrawText(textFont,(messageSelection==0)?Font.CR_WHITE:Font.CR_DARKGREEN,destWidth/2,y,Stringtable.Localize("$TXT_YES"),DTA_VirtualWidth,destWidth,DTA_VirtualHeight,destHeight,DTA_KeepRatio,true);
			screen.DrawText(textFont,(messageSelection==1)?Font.CR_WHITE:Font.CR_DARKGREEN,destWidth/2,y+fontheight,Stringtable.Localize("$TXT_NO"),DTA_VirtualWidth,destWidth,DTA_VirtualHeight,destHeight,DTA_KeepRatio,true);
			if ( messageSelection >= 0 )
			{
				if ( (MenuTime()%8) < 6 )
				{
					screen.DrawText(arrowFont,OptionMenuSettings.mFontColorSelection,(destWidth/2)-11,y+fontheight*messageSelection,selector,DTA_VirtualWidth,destWidth,DTA_VirtualHeight,destHeight,DTA_KeepRatio,true);
				}
			}
		}
	}
}

// TODO BONUS: Code input menu, like those in various oldskool SP campaigns
// except it's somewhat prettier looking and supports alphanumeric input
Class CodeEntryMenu : GenericMenu {}

// Translator display as a menu, allows for more interactivity
Class TranslatorMenu : GenericMenu
{
	bool bShowHint;
	UTranslator trns;
	TextureID thud[2], scroll[4];
	Font tfnt, mfnt, pfnt;
	BrokenLines lines;
	int th, startline[2], maxlines[2], entry;
	CVar ext;
	bool drag;

	private void SetText( String txt )
	{
		String ttxt = (txt.Length()>0)?txt:StringTable.Localize("$TR_NOMSG");
		tfnt = (!mfnt.CanPrint(ttxt))?NewSmallFont:mfnt;
		lines = tfnt.BreakLines(ttxt,200);
		th = tfnt.GetHeight();
		startline[0] = 0;
		startline[1] = 0;
		maxlines[0] = 81/th;
		maxlines[1] = 201/th;
	}

	override void Init( Menu parent )
	{
		Super.Init(parent);
		trns = UTranslator(players[consoleplayer].mo.FindInventory('UTranslator'));
		mfnt = Font.GetFont('UMedFont');
		pfnt = Font.GetFont('UOldTinyFont');
		ext = CVar.FindCVar('sting_transext');
		if ( !trns || !trns.Owner || (trns.Owner.Health <= 0) )
		{
			// don't do anything, will get killed in the next tick
			return;
		}
		MenuSound("menu/activate");
		thud[0] = TexMan.CheckForTexture("TranHUD3",TexMan.Type_Any);
		thud[1] = TexMan.CheckForTexture("TranHUDX",TexMan.Type_Any);
		scroll[0] = TexMan.CheckForTexture("VSldT",TexMan.Type_Any);
		scroll[1] = TexMan.CheckForTexture("VSldM",TexMan.Type_Any);
		scroll[2] = TexMan.CheckForTexture("VSldB",TexMan.Type_Any);
		scroll[3] = TexMan.CheckForTexture("VSldO",TexMan.Type_Any);
		if ( StatusBar is 'UnrealHUD' )
			UnrealHUD(StatusBar).bTranslatorActive = true;
		entry = 0;
		SetText(StringTable.Localize(GetMessage(entry)));
	}

	private String GetMessage( int idx = 0 )
	{
		if ( idx == 0 ) return trns.NewMessage;
		else return trns.OldMessages[trns.OldMessages.Size()-idx];
	}

	private String GetHint( int idx = 0 )
	{
		if ( idx == 0 ) return trns.Hint;
		else return trns.OldHints[trns.OldHints.Size()-idx];
	}

	override void Ticker()
	{
		menuactive = OnNoPause;	// don't pause game while translator is active
		if ( trns && trns.Owner && (trns.Owner.Health > 0) ) return;
		if ( StatusBar is 'UnrealHUD' )
			UnrealHUD(StatusBar).bTranslatorActive = false;
		Close();
	}

	override bool OnUIEvent( UIEvent ev )
	{
		switch ( ev.type )
		{
		case UIEvent.Type_WheelUp:
			if ( startline[ext.GetInt()] > 0 ) MenuSound("menu/cursor");
			startline[ext.GetInt()] = max(0,startline[ext.GetInt()]-1);
			return true;
		case UIEvent.Type_WheelDown:
			if ( startline[ext.GetInt()] < max(0,lines.Count()-maxlines[ext.GetInt()]) ) MenuSound("menu/cursor");
			startline[ext.GetInt()] = min(max(0,lines.Count()-maxlines[ext.GetInt()]),startline[ext.GetInt()]+1);
			return true;
		}
		return Super.OnUIEvent(ev);
	}

	protected bool InBox( Vector2 p, Vector2 a, Vector2 b )
	{
		// pad to make input easier w/ small scales
		a -= (8,8);
		b += (8,8);
		return ((p.x >= a.x) && (p.x < b.x) && (p.y >= a.y) && (p.y < b.y));
	}

	override bool MouseEvent( int type, int mx, int my )
	{
		bool res = Super.MouseEvent(type,mx,my);
		// box locations for various elements
		Vector2 validarea[2], prevmsg[2], nextmsg[2], openhint[2], closehint[2], scrollbar[2];
		double ClipX, ClipY, CurX, CurY;
		Vector2 vpos;
		if ( StatusBar is 'UnrealHUD' )
		{
			ClipX = UnrealHUD(StatusBar).ClipX;
			ClipY = UnrealHUD(StatusBar).ClipY;
			vpos.x = mx/UnrealHUD(StatusBar).scalev.x;
			vpos.y = my/UnrealHUD(StatusBar).scalev.y;
		}
		else
		{
			ClipX = CleanWidth_1;
			ClipY = CleanHeight_1;
			vpos.x = mx/double(CleanXFac_1);
			vpos.y = my/double(CleanYFac_1);
		}
		if ( ext.GetBool() )
		{
			validarea[0] = (ClipX/2-112,ClipY/2-112);
			validarea[1] = validarea[0]+(224,224);
			prevmsg[0] = (ClipX/2-106,ClipY/2+100);
			prevmsg[1] = prevmsg[0]+(4,6);
			String pagestr = String.Format("< %d / %d ",trns.OldMessages.Size()+1-entry,trns.OldMessages.Size()+1);
			int strl = pfnt.StringWidth(pagestr);
			nextmsg[0] = prevmsg[0]+(strl,0);
			nextmsg[1] = nextmsg[0]+(4,6);
			openhint[0] = (ClipX/2+100,ClipY/2+100);
			openhint[1] = openhint[0]+(8,6);
			closehint[0] = (ClipX/2-106,ClipY/2+100);
			closehint[1] = closehint[0]+(8,6);
			scrollbar[0] = (ClipX/2+100,ClipY/2-110);
			scrollbar[1] = scrollbar[0]+(7,208);
		}
		else
		{
			validarea[0] = (ClipX/2-112,ClipY/2-52);
			validarea[1] = validarea[0]+(224,104);
			prevmsg[0] = (ClipX/2-106,ClipY/2+40);
			prevmsg[1] = prevmsg[0]+(4,6);
			String pagestr = String.Format("< %d / %d ",trns.OldMessages.Size()+1-entry,trns.OldMessages.Size()+1);
			int strl = pfnt.StringWidth(pagestr);
			nextmsg[0] = prevmsg[0]+(strl,0);
			nextmsg[1] = nextmsg[0]+(4,6);
			openhint[0] = (ClipX/2+100,ClipY/2+40);
			openhint[1] = openhint[0]+(8,6);
			closehint[0] = (ClipX/2-106,ClipY/2+50);
			closehint[1] = closehint[0]+(8,6);
			scrollbar[0] = (ClipX/2+100,ClipY/2-50);
			scrollbar[1] = scrollbar[0]+(7,88);
		}
		if ( type == MOUSE_Click )
		{
			if ( !InBox(vpos,validarea[0],validarea[1]) ) MenuEvent(MKEY_Back,true);
			else if ( !bShowHint && InBox(vpos,prevmsg[0],prevmsg[1]) ) MenuEvent(MKEY_Left,true);
			else if ( !bShowHint && InBox(vpos,nextmsg[0],nextmsg[1]) ) MenuEvent(MKEY_Right,true);
			else if ( !bShowHint && InBox(vpos,openhint[0],openhint[1]) ) MenuEvent(MKEY_PageDown,true);
			else if ( bShowHint && InBox(vpos,closehint[0],closehint[1]) ) MenuEvent(MKEY_PageUp,true);
			else if ( InBox(vpos,scrollbar[0],scrollbar[1]) ) drag = true;
		}
		else if ( type == MOUSE_Release ) drag = false;
		if ( drag )
		{
			double scl = (ext.GetInt()?192:72)/max(1,lines.Count()-maxlines[ext.GetInt()]);
			int vclamp = int((clamp(vpos.y,scrollbar[0].y+4,scrollbar[1].y-4)-(scrollbar[0].y+4))/scl);
			startline[ext.GetInt()] = clamp(vclamp,0,max(0,lines.Count()-maxlines[ext.GetInt()]));
		}
		return res;
	}

	override bool MenuEvent( int mkey, bool fromcontroller )
	{
		switch( mkey )
		{
		case MKEY_Enter:
		case MKEY_Back:
			if ( StatusBar is 'UnrealHUD' )
				UnrealHUD(StatusBar).bTranslatorActive = false;
			MenuSound(GetCurrentMenu()?"menu/backup":"menu/clear");
			Close();
			return true;
		case MKEY_Up:
			if ( startline[ext.GetInt()] > 0 ) MenuSound("menu/cursor");
			startline[ext.GetInt()] = max(0,startline[ext.GetInt()]-1);
			return true;
		case MKEY_Down:
			if ( startline[ext.GetInt()] < max(0,lines.Count()-maxlines[ext.GetInt()]) ) MenuSound("menu/cursor");
			startline[ext.GetInt()] = min(max(0,lines.Count()-maxlines[ext.GetInt()]),startline[ext.GetInt()]+1);
			return true;
		case MKEY_PageDown:
			if ( trns && (GetHint(entry).length() > 0) )
			{
				MenuSound("menu/cursor");
				bShowHint = true;
				SetText(StringTable.Localize(GetHint(entry)));
			}
			return true;
		case MKEY_PageUp:
			if ( trns && bShowHint )
			{
				MenuSound("menu/cursor");
				bShowHint = false;
				SetText(StringTable.Localize(GetMessage(entry)));
			}
			return true;
		case MKEY_Left:
			if ( trns && (entry < trns.OldMessages.Size()) )
			{
				MenuSound("menu/cursor");
				bShowHint = false;
				entry++;
				SetText(StringTable.Localize(GetMessage(entry)));
			}
			return true;
		case MKEY_Right:
			if ( trns && (entry > 0) )
			{
				MenuSound("menu/cursor");
				bShowHint = false;
				entry--;
				SetText(StringTable.Localize(GetMessage(entry)));
			}
			return true;
		}
		return false;
	}

	override void Drawer()
	{
		Super.Drawer();
		if ( !trns || !trns.Owner || (trns.Owner.Health <= 0) ) return;
		double ClipX, ClipY, CurX, CurY;
		if ( StatusBar is 'UnrealHUD' )
		{
			ClipX = UnrealHUD(StatusBar).ClipX;
			ClipY = UnrealHUD(StatusBar).ClipY;
		}
		else
		{
			ClipX = CleanWidth_1;
			ClipY = CleanHeight_1;
		}
		// The translator
		CurX = ClipX/2-128;
		CurY = ClipY/2-(ext.GetInt()?128:68);
		Screen.DrawTexture(thud[ext.GetInt()],false,CurX,CurY,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true);
		// The message text
		CurX += 22;
		CurY += 22;
		int l = startline[ext.GetInt()];
		for ( int i=0; i<maxlines[ext.GetInt()]; i++ )
		{
			if ( l >= lines.Count() ) break;
			Screen.DrawText(tfnt,Font.CR_UNTRANSLATED,CurX,CurY,lines.StringAt(l),DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true);
			l++;
			CurY += th;
		}
		// Scrollbar
		CurX = ClipX/2+100;
		if ( lines.Count() > maxlines[ext.GetInt()] )
		{
			CurY = ClipY/2-(ext.GetInt()?114:54);
			Screen.DrawTexture(scroll[0],false,CurX,CurY,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true);
			CurY += 8;
			int cnt = ext.GetInt()?25:10;
			for ( int i=0; i<cnt; i++ )
			{
				Screen.DrawTexture(scroll[1],false,CurX,CurY,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true);
				CurY += 8;
			}
			Screen.DrawTexture(scroll[2],false,CurX,CurY,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true);
			CurY = (ClipY/2-(ext.GetInt()?106:46)) + ((startline[ext.GetInt()]*(ext.GetInt()?192:72))/max(1,lines.Count()-maxlines[ext.GetInt()]));
			Screen.DrawTexture(scroll[3],false,CurX,CurY,DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true);
		}
		if ( !bShowHint && (GetHint(entry).length() > 0) )
		{
			CurY = ClipY/2+(ext.GetInt()?100:40);
			Screen.DrawText(pfnt,Font.CR_GREEN,CurX,CurY,">>",DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true,DTA_Alpha,(MenuTime()%16)/16.);
		}
		CurX = ClipX/2-106;
		CurY = ClipY/2+(ext.GetInt()?100:40);
		if ( bShowHint ) Screen.DrawText(pfnt,Font.CR_GREEN,CurX,CurY,"<<",DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true,DTA_Alpha,(MenuTime()%16)/16.);
		else if ( trns.OldMessages.Size() > 0 ) Screen.DrawText(pfnt,Font.CR_GREEN,CurX,CurY,String.Format("%s %d / %d %s",(entry<trns.OldMessages.Size())?"<":" ",trns.OldMessages.Size()+1-entry,trns.OldMessages.Size()+1,(entry>0)?">":" "),DTA_VirtualWidthF,ClipX,DTA_VirtualHeightF,ClipY,DTA_KeepRatio,true);
	}
}
