/** 
 * @file llstartup.cpp
 * @brief startup routines.
 *
 * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
 * $License$
 */

#include "llviewerprecompiledheaders.h"

#include "llstartup.h"

#if LL_WINDOWS
#include <process.h>		// _spawnl()
#else
#include <sys/stat.h>		// mkdir()
#endif

#include "audioengine.h"

#if LL_FMOD
#include "audioengine_fmod.h"
#endif

#include "audiosettings.h"
#include "llcachename.h"
#include "llviewercontrol.h"
#include "llcrypto.h"
#include "lldir.h"
#include "lleconomy.h"
#include "llfiltersd2xmlrpc.h"
#include "llfocusmgr.h"
#include "imageids.h"
#include "lllandmark.h"
#include "llloginflags.h"
#include "llmd5.h"
#include "llmemorystream.h"
#include "llregionhandle.h"
#include "llsd.h"
#include "llsdserialize.h"
#include "llsdutil.h"
#include "llsecondlifeurls.h"
#include "llstring.h"
#include "lluserrelations.h"
#include "llversion.h"
#include "llvfs.h"
#include "llwindow.h"		// for shell_open
#include "message.h"
#include "v3math.h"

#include "llagent.h"
#include "llagentpilot.h"
#include "llasynchostbyname.h"
#include "llfloateravatarpicker.h"
#include "llcallbacklist.h"
#include "llcallingcard.h"
#include "llcolorscheme.h"
#include "llconsole.h"
#include "llcontainerview.h"
#include "lldebugview.h"
#include "lldrawable.h"
#include "lleventnotifier.h"
#include "llface.h"
#include "llfeaturemanager.h"
#include "llfloateraccounthistory.h"
#include "llfloaterchat.h"
#include "llfloatergesture.h"
#include "llfloaterland.h"
#include "llfloatertopobjects.h"
#include "llfloaterrate.h"
#include "llfloatertos.h"
#include "llfloaterworldmap.h"
#include "llframestats.h"
#include "llframestatview.h"
#include "llgesturemgr.h"
#include "llgroupmgr.h"
#include "llhudeffecttrail.h"
#include "llhudmanager.h"
#include "llhttpclient.h"
#include "llimagebmp.h"
#include "llinventorymodel.h"
#include "llinventoryview.h"
#include "llkeyboard.h"
#include "llpanellogin.h"
#include "llmutelist.h"
#include "llnotify.h"
#include "llpanelavatar.h"
#include "llpaneldirbrowser.h"
#include "llpaneldirland.h"
#include "llpanelevent.h"
#include "llpanelclassified.h"
#include "llpanelpick.h"
#include "llpanelplace.h"
#include "llpanelgrouplandmoney.h"
#include "llpanelgroupnotices.h"
#include "llpreview.h"
#include "llpreviewscript.h"
#include "llselectmgr.h"
#include "llsky.h"
#include "llstatview.h"
#include "llsurface.h"
#include "lltoolmgr.h"
#include "llui.h"
#include "llurlwhitelist.h"
#include "lluserauth.h"
#include "llviewerassetstorage.h"
#include "llviewercamera.h"
#include "llviewerdisplay.h"
#include "llviewergesture.h"
#include "llviewerimagelist.h"
#include "llviewermedialist.h"
#include "llviewermenu.h"
#include "llviewermessage.h"
#include "llviewernetwork.h"
#include "llviewerobjectlist.h"
#include "llviewerparcelmgr.h"
#include "llviewerregion.h"
#include "llviewerstats.h"
#include "llviewerthrottle.h"
#include "llviewerwindow.h"
#include "llvoavatar.h"
#include "llvoclouds.h"
#include "llworld.h"
#include "llworldmap.h"
#include "llxfermanager.h"
#include "pipeline.h"
#include "viewer.h"
#include "llmediaengine.h"
#include "llfasttimerview.h"
#include "llmozlib.h"
#include "llweb.h"
#include "llfloaterhtml.h"

#if LL_WINDOWS
#include "llwindebug.h"
#include "lldxhardware.h"
#endif

#if LL_QUICKTIME_ENABLED
#if LL_DARWIN
#include <QuickTime/QuickTime.h>
#else
// quicktime specific includes
#include "MacTypes.h"
#include "QTML.h"
#include "Movies.h"
#include "FixMath.h"
#endif
#endif

//
// exported globals
//

// HACK: Allow server to change sun and moon IDs.
// I can't figure out how to pass the appropriate
// information into the LLVOSky constructor.  JC
LLUUID gSunTextureID = IMG_SUN;
LLUUID gMoonTextureID = IMG_MOON;
LLUUID gCloudTextureID = IMG_CLOUD_POOF;

const char* SCREEN_HOME_FILENAME = "screen_home.bmp";
const char* SCREEN_LAST_FILENAME = "screen_last.bmp";

//
// Imported globals
//
extern LLPointer<LLImageGL> gStartImageGL;
extern S32 gStartImageWidth;
extern S32 gStartImageHeight;
extern std::string gSerialNumber;

//
// local globals
//

static LLHost gAgentSimHost;
static BOOL gSkipOptionalUpdate = FALSE;

bool gQuickTimeInitialized = false;
static bool gGotUseCircuitCodeAck = false;
LLString gInitialOutfit;
LLString gInitialOutfitGender;	// "male" or "female"


//
// local function declaration
//

void login_show();
void login_callback(S32 option, void* userdata);
LLString load_password_from_disk();
void save_password_to_disk(const char* hashed_password);
BOOL is_hex_string(U8* str, S32 len);
void show_first_run_dialog();
void first_run_dialog_callback(S32 option, void* userdata);
void set_startup_status(const F32 frac, const char* string, const char* msg);
void on_userserver_name_resolved( BOOL success, const LLString& host_name, U32 ip, void* userdata );
void login_alert_status(S32 option, void* user_data);
void update_app(BOOL mandatory, const std::string& message);
void update_dialog_callback(S32 option, void *userdata);
void login_packet_failed(void**, S32 result);
void use_circuit_callback(void**, S32 result);
void register_viewer_callbacks(LLMessageSystem* msg);
void init_stat_view();
void asset_callback_nothing(LLVFS*, const LLUUID&, LLAssetType::EType, void*, S32);
void dialog_choose_gender_first_start();
void callback_choose_gender(S32 option, void* userdata);
void init_start_screen(S32 location_id);
void release_start_screen();
void process_connect_to_userserver(LLMessageSystem* msg, void**);


//
// exported functionality
//

//
// local classes
//
class LLGestureInventoryFetchObserver : public LLInventoryFetchObserver
{
public:
	LLGestureInventoryFetchObserver() {}
	virtual void done()
	{
		// we've downloaded all the items, so repaint the dialog
		LLFloaterGesture::refreshAll();

		gInventory.removeObserver(this);
		delete this;
	}
};



// Returns FALSE to skip other idle processing. Should only return
// TRUE when all initialization done.
BOOL idle_startup()
{
	LLMemType mt1(LLMemType::MTYPE_STARTUP);
	
	const F32 PRECACHING_DELAY = gSavedSettings.getF32("PrecachingDelay");
	const F32 TIMEOUT_SECONDS = 5.f;
	const S32 MAX_TIMEOUT_COUNT = 3;
	static LLTimer timeout;
	static S32 timeout_count = 0;

	static LLTimer login_time;

	// until this is encapsulated, this little hack for the
	// auth/transform loop will do.
	static F32 progress = 0.10f;

	static std::string auth_uri;
	static std::string auth_method;
	static std::string auth_desc;
	static std::string auth_message;
	static LLString firstname;
	static LLString lastname;
	static LLString password;
	static std::vector<const char*> requested_options;

	static U32 region_size = 256;
	static F32 region_scale = 1.f;
	static U64 first_sim_handle = 0;
	static LLHost first_sim;
	static std::string first_sim_seed_cap;

	static LLVector3 initial_sun_direction(1.f, 0.f, 0.f);
	static LLVector3 agent_start_position_region(10.f, 10.f, 10.f);		// default for when no space server
	static LLVector3 agent_start_look_at(1.0f, 0.f, 0.f);
	static std::string agent_start_location = "safe";

	// last location by default
	static S32  agent_location_id = START_LOCATION_ID_LAST;
	static S32  location_which = START_LOCATION_ID_LAST;

	static BOOL show_connect_box = TRUE;
	static BOOL remember_password = TRUE;

	static BOOL stipend_since_login = FALSE;

	static BOOL samename = FALSE;

	static BOOL did_precache = FALSE;
	
	BOOL do_normal_idle = FALSE;

	// HACK: These are things from the main loop that usually aren't done
	// until initialization is complete, but need to be done here for things
	// to work.
	gIdleCallbacks.callFunctions();
	gViewerWindow->handlePerFrameHover();
	LLMortician::updateClass();

	if (gNoRender)
	{
		// HACK, skip optional updates if you're running drones
		gSkipOptionalUpdate = TRUE;
	}
	else
	{
		// Update images?
		gImageList.updateImages(0.01f);
	}

	if (STATE_FIRST == gStartupState)
	{
		gViewerWindow->showCursor();
		gViewerWindow->getWindow()->setCursor(UI_CURSOR_WAIT);

		/////////////////////////////////////////////////
		//
		// Initialize stuff that doesn't need data from userserver/simulators
		//

		if (gFeatureManagerp->isSafe())
		{
			gViewerWindow->alertXml("DisplaySetToSafe");
		}
		else if (gSavedSettings.getS32("LastFeatureVersion") < gFeatureManagerp->getVersion())
		{
			if (gSavedSettings.getS32("LastFeatureVersion") != 0)
			{
				gViewerWindow->alertXml("DisplaySetToRecommended");
			}
		}
		gSavedSettings.setS32("LastFeatureVersion", gFeatureManagerp->getVersion());

		LLString xml_file = LLUI::locateSkin("xui_version.xml");
		LLXMLNodePtr root;
		bool xml_ok = false;
		if (LLXMLNode::parseFile(xml_file, root, NULL))
		{
			if( (root->hasName("xui_version") ) )
			{
				LLString value = root->getValue();
				F32 version = 0.0f;
				LLString::convertToF32(value, version);
				if (version >= 1.0f)
				{
					xml_ok = true;
				}
			}
		}
		if (!xml_ok)
		{
			// XUI:translate (maybe - very unlikely error message)
			// Note: alerts.xml may be invalid - if this gets translated it will need to be in the code
			LLString bad_xui_msg = "An error occured while updating Second Life. Please download the latest version from www.secondlife.com.";
			app_early_exit(bad_xui_msg);
		}
		//
		// Statistics stuff
		//

		// Load autopilot and stats stuff
		gAgentPilot.load(gSavedSettings.getString("StatsPilotFile").c_str());
		gFrameStats.setFilename(gSavedSettings.getString("StatsFile"));
		gFrameStats.setSummaryFilename(gSavedSettings.getString("StatsSummaryFile"));

		//gErrorStream.setTime(gSavedSettings.getBOOL("LogTimestamps"));

		// Load the throttle settings
		gViewerThrottle.load();

		//
		// Initialize messaging system
		//
		llinfos << "Initializing messaging system..." << llendl;

		std::string message_template_path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"message_template.msg");

		FILE* found_template = NULL;
		found_template = LLFile::fopen(message_template_path.c_str(), "r");
		if (found_template)
		{
			fclose(found_template);

			if(!start_messaging_system(
				   message_template_path,
				   gAgent.mViewerPort,
				   LL_VERSION_MAJOR,
				   LL_VERSION_MINOR,
				   LL_VERSION_PATCH,
				   FALSE,
				   std::string()))
			{
				std::string msg = llformat("Unable to start networking, error %d", gMessageSystem->getErrorCode());
				app_early_exit(msg);
			}
		}
		else
		{
			app_early_exit("Unable to initialize communications.");
		}

		if(gMessageSystem && gMessageSystem->isOK())
		{
			// Initialize all of the callbacks in case of bad message
			// system data
			LLMessageSystem* msg = gMessageSystem;
			msg->setExceptionFunc(MX_UNREGISTERED_MESSAGE,
								  invalid_message_callback,
								  NULL);
			msg->setExceptionFunc(MX_PACKET_TOO_SHORT,
								  invalid_message_callback,
								  NULL);
			msg->setExceptionFunc(MX_RAN_OFF_END_OF_PACKET,
								  invalid_message_callback,
								  NULL);
			msg->setExceptionFunc(MX_WROTE_PAST_BUFFER_SIZE,
								  invalid_message_callback,
								  NULL);

			gErrorStream.setUTCTimestamp(gLogUTC);
			if (gSavedSettings.getBOOL("LogMessages") || gLogMessages)
			{
				llinfos << "Message logging activated!" << llendl;
				msg->startLogging();
			}

			// start the xfer system. by default, choke the downloads
			// a lot...
			const S32 VIEWER_MAX_XFER = 3;
			start_xfer_manager(gVFS);
			gXferManager->setMaxIncomingXfers(VIEWER_MAX_XFER);
			F32 xfer_throttle_bps = gSavedSettings.getF32("XferThrottle");
			if (xfer_throttle_bps > 1.f)
			{
				gXferManager->setUseAckThrottling(TRUE);
				gXferManager->setAckThrottleBPS(xfer_throttle_bps);
			}
			gAssetStorage = new LLViewerAssetStorage(msg, gXferManager, gVFS, gUserServer);

			msg->mPacketRing.setDropPercentage(gPacketDropPercentage);
			if (gInBandwidth != 0.f)
			{
				llinfos << "Setting packetring incoming bandwidth to " << gInBandwidth << llendl;
				msg->mPacketRing.setUseInThrottle(TRUE);
				msg->mPacketRing.setInBandwidth(gInBandwidth);
			}
			if (gOutBandwidth != 0.f)
			{
				llinfos << "Setting packetring outgoing bandwidth to " << gOutBandwidth << llendl;
				msg->mPacketRing.setUseOutThrottle(TRUE);
				msg->mPacketRing.setOutBandwidth(gOutBandwidth);
			}
		}

		// initialize the economy
		gGlobalEconomy = new LLGlobalEconomy();

		//---------------------------------------------------------------------
		// LibXUL (Mozilla) initialization
		//---------------------------------------------------------------------
		#if LL_LIBXUL_ENABLED
		set_startup_status(0.48f, "Initializing embedded web browser...", gAgent.mMOTD.c_str());
		display_startup();

		#if LL_DARWIN
			// For Mac OS, we store both the shared libraries and the runtime files (chrome/, plugins/, etc) in
			// Second Life.app/Contents/MacOS/.  This matches the way Firefox is distributed on the Mac.
			std::string profileBaseDir(gDirUtilp->getExecutableDir());
		#else
			std::string profileBaseDir( gDirUtilp->getExpandedFilename( LL_PATH_APP_SETTINGS, "" ) );
			profileBaseDir += gDirUtilp->getDirDelimiter();
			#ifdef LL_DEBUG
			profileBaseDir += "mozilla_debug";
			#else
			profileBaseDir += "mozilla";
			#endif
		#endif
		LLMozLib::getInstance()->init( profileBaseDir, gDirUtilp->getExpandedFilename( LL_PATH_MOZILLA_PROFILE, "" ) ); 

		std::ostringstream codec;
		codec << "[Second Life " << LL_VERSION_MAJOR << "." << LL_VERSION_MINOR << "." << LL_VERSION_PATCH << "." << LL_VERSION_BUILD << "]";
		LLMozLib::getInstance()->setBrowserAgentId( codec.str() );
		#endif

		//-------------------------------------------------
		// Init audio, which may be needed for prefs dialog
		// or audio cues in connection UI.
		//-------------------------------------------------

		if (gUseAudio)
		{
#if LL_FMOD
			gAudiop = (LLAudioEngine *) new LLAudioEngine_FMOD();
#else
			gAudiop = NULL;
#endif

			if (gAudiop)
			{
#if LL_WINDOWS
				// FMOD on Windows needs the window handle to stop playing audio
				// when window is minimized. JC
				void* window_handle = (void*)llwindow_get_hwnd(gViewerWindow->getWindow());
#else
				void* window_handle = NULL;
#endif
				BOOL init = gAudiop->init(kAUDIO_NUM_SOURCES, window_handle);
				if(!init)
				{
					llwarns << "Unable to initialize audio engine" << llendl;
				}
				gAudiop->setMuted(TRUE);
			}
		}

		if (LLTimer::knownBadTimer())
		{
			llwarns << "Unreliable timers detected (may be bad PCI chipset)!!" << llendl;
		}

		// Get ready to show the login dialog
		if (!gConnectToSomething)
		{
			// Don't use a session token, and generate a random user id
			gAgentID.generate();
			gAgentSessionID = LLUUID::null;

			gStartupState = STATE_WORLD_INIT;
			return do_normal_idle;
		}
		else if (!gRunLocal)
		{
			//
			// Log on to userserver
			//
			if( !gCmdLineFirstName.empty() 
				&& !gCmdLineLastName.empty() 
				&& !gCmdLinePassword.empty())
			{
				firstname = gCmdLineFirstName;
				lastname = gCmdLineLastName;

				LLMD5 pass((unsigned char*)gCmdLinePassword.c_str());
				char md5pass[33];
				pass.hex_digest(md5pass);
				password = md5pass;

				remember_password = gSavedSettings.getBOOL("RememberPassword");
				show_connect_box = FALSE;
			}
			else if (gAutoLogin || gSavedSettings.getBOOL("AutoLogin"))
			{
				firstname = gSavedSettings.getString("FirstName");
				lastname = gSavedSettings.getString("LastName");
				password = load_password_from_disk();
				remember_password = TRUE;
				show_connect_box = FALSE;
			}
			else
			{
				// if not automatically logging in, display login dialog
				// until a valid userserver is selected
				firstname = gSavedSettings.getString("FirstName");
				lastname = gSavedSettings.getString("LastName");
				password = load_password_from_disk();
				remember_password = gSavedSettings.getBOOL("RememberPassword");
				show_connect_box = TRUE;
			}

			// Go to the next startup state
			gStartupState++;
			return do_normal_idle;
		}
		else
		{
			gStartupState++;
			return do_normal_idle;
		}
	}

	if (STATE_LOGIN_SHOW == gStartupState)
	{
		gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW);
		// Push our window frontmost
		gViewerWindow->getWindow()->show();

		timeout_count = 0;

		if (gConnectToSomething && !gRunLocal)
		{
			if (show_connect_box)
			{
				if (gNoRender)
				{
					llerrs << "Need to autologin or use command line with norender!" << llendl;
				}
				// Make sure the process dialog doesn't hide things
				gViewerWindow->setShowProgress(FALSE);

				// Show the login dialog
				login_show();

				// connect dialog is already shown, so fill in the names
				LLPanelLogin::setFields( firstname, lastname, password, remember_password );
				LLPanelLogin::giveFocus();

				gSavedSettings.setBOOL("FirstRunThisInstall", FALSE);

				gStartupState++;
			}
			else
			{
				// skip directly to message template verification
				gStartupState = STATE_LOGIN_CLEANUP;
			}
		}
		else
		{
			gMessageSystem->setCircuitProtection(FALSE);
			gStartupState = STATE_LOGIN_CLEANUP;
		}

		timeout.reset();
		return do_normal_idle;
	}

	if (STATE_LOGIN_WAIT == gStartupState)
	{
		// Don't do anything.  Wait for the login view to call the login_callback,
		// which will push us to the next state.

		// Sleep so we don't spin the CPU
		ms_sleep(1);
		return do_normal_idle;
	}

	if (STATE_LOGIN_CLEANUP == gStartupState)
	{
		if (show_connect_box)
		{
			// Load all the name information out of the login view
			LLPanelLogin::getFields(firstname, lastname, password, remember_password);

			// HACK: Try to make not jump on login
			gKeyboard->resetKeys();
		}

		if (!firstname.empty() && !lastname.empty())
		{
			gSavedSettings.setString("FirstName", firstname);
			gSavedSettings.setString("LastName", lastname);

			llinfos << "Attempting login as: " << firstname << " " << lastname << llendl;
			write_debug("Attempting login as: ");
			write_debug(firstname);
			write_debug(" ");
			write_debug(lastname);
			write_debug("\n");
		}

		// create necessary directories
		// FIXME: these mkdir's should error check
		gDirUtilp->setLindenUserDir(firstname.c_str(), lastname.c_str());


		LLFile::mkdir(gDirUtilp->getLindenUserDir().c_str());

		// the mute list is loaded in the llmutelist class.

		gSavedSettings.loadFromFile(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT,"overrides.xml"));

		// handle the per account settings setup
		strcpy(gPerAccountSettingsFileName, gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, DEFAULT_SETTINGS_FILE).c_str());

		// per account settings.  Set defaults here if not found.  If we get a bunch of these, eventually move to a function.
		gSavedPerAccountSettings.loadFromFile(gPerAccountSettingsFileName);

		// Need to set the LastLogoff time here if we don't have one.  LastLogoff is used for "Recent Items" calculation
		// and startup time is close enough if we don't have a real value.
		if (gSavedPerAccountSettings.getU32("LastLogoff") == 0)
		{
			gSavedPerAccountSettings.setU32("LastLogoff", time_corrected());
		}

		//Default the path if one isn't set.
		if (gSavedPerAccountSettings.getString("InstantMessageLogPath").empty())
		{
			gDirUtilp->setChatLogsDir(gDirUtilp->getOSUserAppDir());
			gSavedPerAccountSettings.setString("InstantMessageLogPath",gDirUtilp->getChatLogsDir());
		}
		else
		{
			gDirUtilp->setChatLogsDir(gSavedPerAccountSettings.getString("InstantMessageLogPath"));		
		}
		
		gDirUtilp->setPerAccountChatLogsDir(firstname.c_str(), lastname.c_str());

		LLFile::mkdir(gDirUtilp->getChatLogsDir().c_str());
		LLFile::mkdir(gDirUtilp->getPerAccountChatLogsDir().c_str());

		
#if LL_WINDOWS
		if (gSavedSettings.getBOOL("UseDebugLogin") && show_connect_box)
#else
		if (show_connect_box)
#endif
		{
			LLString server_label;
			S32 domain_name_index;
			LLPanelLogin::getServer( server_label, domain_name_index );
			gUserServerChoice = (EUserServerDomain) domain_name_index;
			gSavedSettings.setS32("ServerChoice", gUserServerChoice);
			if (gUserServerChoice == USERSERVER_OTHER)
			{
				gUserServer.setHostByName( server_label.c_str() );
				snprintf(gUserServerName, MAX_STRING, "%s", server_label.c_str());
			}
		}

		if (show_connect_box)
		{
			LLString location;
			LLPanelLogin::getLocation( location );
			LLURLSimString::setString( location );
			LLPanelLogin::close();
		}

		//For HTML parsing in text boxes.
		LLTextEditor::setLinkColor( gSavedSettings.getColor4("HTMLLinkColor") );
		LLTextEditor::setURLCallbacks ( &LLWeb::loadURL, &process_secondlife_url );

		//-------------------------------------------------
		// Handle startup progress screen
		//-------------------------------------------------

		// on startup the user can request to go to their home,
		// their last location, or some URL "-url //sim/x/y[/z]"
		// All accounts have both a home and a last location, and we don't support
		// more locations than that.  Choose the appropriate one.  JC
		if (LLURLSimString::parse())
		{
			// a startup URL was specified
			agent_location_id = START_LOCATION_ID_TELEHUB;

			// doesn't really matter what location_which is, since
			// agent_start_look_at will be overwritten when the
			// UserLoginLocationReply arrives
			location_which = START_LOCATION_ID_LAST;
		}
		else if (gSavedSettings.getBOOL("LoginLastLocation"))
		{
			agent_location_id = START_LOCATION_ID_LAST;	// last location
			location_which = START_LOCATION_ID_LAST;
		}
		else
		{
			agent_location_id = START_LOCATION_ID_HOME;	// home
			location_which = START_LOCATION_ID_HOME;
		}

		gViewerWindow->getWindow()->setCursor(UI_CURSOR_WAIT);

		if (!gNoRender)
		{
			init_start_screen(agent_location_id);
		}

		// Display the startup progress bar.
		gViewerWindow->setShowProgress(TRUE);
		gViewerWindow->setProgressCancelButtonVisible(TRUE, "Quit");

		// Poke the VFS, which could potentially block for a while if
		// Windows XP is acting up
		set_startup_status(0.05f, "Verifying cache files (can take 60-90 seconds)...", NULL);
		display_startup();

		gVFS->pokeFiles();

		// color init must be after saved settings loaded
		init_colors();

		// Request userserver domain name
		set_startup_status(0.05f, "Finding Server Domain Name...", NULL);

		// We're prematurely switching out of this state because the
		// userserver name resolver can potentiallly occur before reaching the end of the
		// switch statement.  Also, if it's done at the bottom, sometimes we will
		// skip the userserver resolved step (in the local cases) - djs 09/24/03
		gStartupState++;
		timeout.reset();

		switch( gUserServerChoice )
		{
		case USERSERVER_AGNI:
			gInProductionGrid = TRUE;
		case USERSERVER_DMZ:
		case USERSERVER_ADITI:
		case USERSERVER_SIVA:
		case USERSERVER_SHAKTI:
		case USERSERVER_DURGA:
		case USERSERVER_SOMA:
		case USERSERVER_GANGA:
		{
				const char* host_name = gUserServerDomainName[gUserServerChoice].mName;
				sprintf(gUserServerName,"%s", host_name);
				llinfos << "Resolving " <<
					gUserServerDomainName[gUserServerChoice].mLabel <<
					" userserver domain name " << host_name << llendl;

				BOOL requested_domain_name = gAsyncHostByName.startRequest( host_name, on_userserver_name_resolved, NULL );
				if( !requested_domain_name )
				//BOOL resolved_domain_name = gUserServer.setHostByName( host_name );
				//if( !resolved_domain_name )
				{
					llwarns << "setHostByName failed" << llendl;
					
					LLStringBase<char>::format_map_t args;
					args["[HOST_NAME]"] = host_name;

					gViewerWindow->alertXml("UnableToConnect", args, login_alert_done );
					gStartupState = STATE_LOGIN_SHOW;
					return FALSE;
				}
				break;
			}

		case USERSERVER_LOCAL:
			llinfos << "Using local userserver" << llendl;
			gUserServer.setAddress( LOOPBACK_ADDRESS_STRING );
			gStartupState = STATE_USERSERVER_RESOLVED;
			break;

		case USERSERVER_OTHER:
			llinfos << "Userserver set explicitly" << llendl;
			gStartupState = STATE_USERSERVER_RESOLVED;
			break;

		case USERSERVER_NONE:
		default:
			llerrs << "No userserver IP address specified" << llendl;
			break;
		}
		return do_normal_idle;
	}

	if (STATE_RESOLVING_USERSERVER == gStartupState)
	{
		// Don't do anything.  Wait for LL_WM_HOST_RESOLVED which is handled by LLAsyncHostByName,
		// which calls on_userserver_name_resolved, which will push us to the next state.
		if (timeout.getElapsedTimeF32() > TIMEOUT_SECONDS*3.f)
		{
			// Cancel the pending asynchostbyname request

			gViewerWindow->alertXml("CanNotFindServer",
				login_alert_status, NULL);

			// Back up to login screen
			gStartupState = STATE_LOGIN_SHOW;
			gViewerStats->incStat(LLViewerStats::ST_LOGIN_TIMEOUT_COUNT);
		}
		ms_sleep(1);
		return do_normal_idle;
	}

	if (STATE_USERSERVER_RESOLVED == gStartupState)
	{
		if (!gUserServer.isOk())
		{
			LLStringBase<char>::format_map_t args;
			args["[IP_ADDRESS]"] = u32_to_ip_string( gUserServer.getAddress() );

			gViewerWindow->alertXml("PleaseSelectServer", args, login_alert_done );

			gStartupState = STATE_LOGIN_SHOW;
			return FALSE;
		}

		write_debug("Userserver: ");
		char tmp_str[256];
		gUserServer.getIPString(tmp_str, 256);
		write_debug(tmp_str);
		write_debug("\n");

		gStartupState++;
	}

	if (STATE_MESSAGE_TEMPLATE_SEND == gStartupState)
	{
		set_startup_status(0.10f, "Verifying protocol version...", NULL);

		LLHost mt_host;
		if (!gRunLocal)
		{
			// open up user server circuit (trusted)
			gMessageSystem->enableCircuit(gUserServer, TRUE);
			mt_host = gUserServer;
		}
		else
		{
			mt_host = gAgentSimHost;
		}

		llinfos << "Verifying message template..." << llendl;

		// register with the message system so it knows we're
		// expecting this message
		LLMessageSystem* msg = gMessageSystem;
		msg->setHandlerFuncFast(_PREHASH_TemplateChecksumReply, null_message_callback, NULL);
		msg->newMessageFast(_PREHASH_SecuredTemplateChecksumRequest);
		msg->nextBlockFast(_PREHASH_TokenBlock);
		lldebugs << "random token: " << gTemplateToken << llendl;
		msg->addUUIDFast(_PREHASH_Token, gTemplateToken);
		msg->sendReliable(mt_host);

		timeout.reset();
		gStartupState++;
		return do_normal_idle;
	}

	if (STATE_MESSAGE_TEMPLATE_WAIT == gStartupState)
	{
		U32 remote_template_checksum = 0;

		U8 major_version = 0;
		U8 minor_version = 0;
		U8 patch_version = 0;
		U8 server_version = 0;
		U32 flags = 0x0;

		LLMessageSystem* msg = gMessageSystem;
		while (msg->checkMessages(gFrameCount))
		{
			if (msg->isMessageFast(_PREHASH_TemplateChecksumReply))
			{
				LLUUID token;
				msg->getUUID("TokenBlock", "Token", token);
				if(token != gTemplateToken)
				{
					llwarns << "Incorrect token in template checksum reply: "
							<< token << llendl;
					return do_normal_idle;
				}
				msg->getU32("DataBlock", "Checksum", remote_template_checksum);
				msg->getU8 ("DataBlock", "MajorVersion", major_version);
				msg->getU8 ("DataBlock", "MinorVersion", minor_version);
				msg->getU8 ("DataBlock", "PatchVersion", patch_version);
				msg->getU8 ("DataBlock", "ServerVersion", server_version);
				msg->getU32("DataBlock", "Flags", flags);

				BOOL update_available = FALSE;
				BOOL mandatory = FALSE;

				if (remote_template_checksum != msg->mMessageFileChecksum)
				{
					llinfos << "Message template out of sync" << llendl;
					// Mandatory update -- message template checksum doesn't match
					update_available = TRUE;
					mandatory = TRUE;
				}

				BOOL quit = FALSE;
				if (update_available)
				{
					if (show_connect_box)
					{
						update_app(mandatory, "");
						gStartupState = STATE_UPDATE_CHECK;
						return FALSE;
					}
					else
					{
						quit = TRUE;
					}
				}
				// Bail out and clean up circuit
				if (quit)
				{
					msg->newMessageFast(_PREHASH_CloseCircuit);
					msg->sendMessage( msg->getSender() );
					app_force_quit(NULL);
					return FALSE;
				}

				// If we get here, we've got a compatible message template

				if (!mandatory)
				{
					llinfos << "Message template is current!" << llendl;
				}
				gStartupState = STATE_LOGIN_AUTH_INIT;
				timeout.reset();
				// unregister with the message system so it knows we're no longer expecting this message
				msg->setHandlerFuncFast(_PREHASH_TemplateChecksumReply, NULL,	NULL);

				msg->newMessageFast(_PREHASH_CloseCircuit);
				msg->sendMessage(gUserServer);
				msg->disableCircuit(gUserServer);
				if (gRunLocal)
				{
					msg->enableCircuit(gAgentSimHost, TRUE);

					// Don't use a session token, and generate a random user id
					gAgentID.generate();
					gAgentSessionID = LLUUID::null;

					// Skip userserver queries.
					gStartupState = STATE_WORLD_INIT;
				}
			}
		}
		gMessageSystem->processAcks();

		if (timeout.getElapsedTimeF32() > TIMEOUT_SECONDS)
		{
			if (timeout_count > MAX_TIMEOUT_COUNT)
			{
				gViewerWindow->alertXml("SystemMayBeDown",
					login_alert_status,
					NULL);

				// Back up to login screen
				gStartupState = STATE_LOGIN_SHOW;
				gViewerStats->incStat(LLViewerStats::ST_LOGIN_TIMEOUT_COUNT);
			}
			else
			{
				llinfos << "Resending on timeout" << llendl;
				gStartupState--;
				timeout_count++;
			}
		}

		return do_normal_idle;
	}

	if (STATE_UPDATE_CHECK == gStartupState)
	{
		return do_normal_idle;
	}

	if(STATE_LOGIN_AUTH_INIT == gStartupState)
	{
//#define LL_MINIMIAL_REQUESTED_OPTIONS
		lldebugs << "STATE_LOGIN_AUTH_INIT" << llendl;
		if (!gUserAuthp)
		{
			gUserAuthp = new LLUserAuth();
		}
		requested_options.clear();
		requested_options.push_back("inventory-root");
		requested_options.push_back("inventory-skeleton");
		//requested_options.push_back("inventory-meat");
		//requested_options.push_back("inventory-skel-targets");
#if (!defined LL_MINIMIAL_REQUESTED_OPTIONS)
		if(gRequestInventoryLibrary)
		{
			requested_options.push_back("inventory-lib-root");
			requested_options.push_back("inventory-lib-owner");
			requested_options.push_back("inventory-skel-lib");
		//	requested_options.push_back("inventory-meat-lib");
		}

		requested_options.push_back("initial-outfit");
		requested_options.push_back("gestures");
		requested_options.push_back("event_categories");
		requested_options.push_back("event_notifications");
		requested_options.push_back("classified_categories");
		//requested_options.push_back("inventory-targets");
		requested_options.push_back("buddy-list");
		requested_options.push_back("ui-config");
#endif
		requested_options.push_back("login-flags");
		requested_options.push_back("global-textures");
		if(gGodConnect)
		{
			gSavedSettings.setBOOL("UseDebugMenus", TRUE);
			requested_options.push_back("god-connect");
		}
		auth_uri = getLoginURI();
		auth_method = "login_to_simulator";
		auth_desc = "Logging in.  ";
		auth_desc += gSecondLife;
		auth_desc += " may appear frozen.  Please wait.";
		++gStartupState;
	}

	if (STATE_LOGIN_AUTHENTICATE == gStartupState)
	{
		lldebugs << "STATE_LOGIN_AUTHENTICATE" << llendl;
		set_startup_status(progress, auth_desc.c_str(), auth_message.c_str());
		progress += 0.02f;
		display_startup();
		
		std::stringstream start;
		if (LLURLSimString::parse())
		{
			// a startup URL was specified
			std::stringstream unescaped_start;
			unescaped_start << "uri:" 
				<< LLURLSimString::sInstance.mSimName << "&" 
				<< LLURLSimString::sInstance.mX << "&" 
				<< LLURLSimString::sInstance.mY << "&" 
				<< LLURLSimString::sInstance.mZ;
			start << xml_escape_string(unescaped_start.str().c_str());
		}
		else if (gSavedSettings.getBOOL("LoginLastLocation"))
		{
			start << "last";
		}
		else
		{
			start << "home";
		}

		char hashed_mac_string[MD5HEX_STR_SIZE];
		LLMD5 hashed_mac;
		hashed_mac.update( gMACAddress, MAC_ADDRESS_BYTES );
		hashed_mac.finalize();
		hashed_mac.hex_digest(hashed_mac_string);
		
		gUserAuthp->authenticate(
			auth_uri.c_str(),
			auth_method.c_str(),
			firstname.c_str(),
			lastname.c_str(),
			password.c_str(),
			start.str().c_str(),
			gSkipOptionalUpdate,
			gAcceptTOS,
			gAcceptCriticalMessage,
			gViewerDigest,
			gLastExecFroze,
			requested_options,
			hashed_mac_string,
			gSerialNumber);
		// reset globals
		gAcceptTOS = FALSE;
		gAcceptCriticalMessage = FALSE;
		++gStartupState;
		return do_normal_idle;
	}

	if(STATE_LOGIN_NO_DATA_YET == gStartupState)
	{
		//lldebugs << "STATE_LOGIN_NO_DATA_YET" << llendl;
		if (!gUserAuthp)
		{
			llerrs << "No userauth in STATE_LOGIN_NO_DATA_YET!" << llendl;
		}
		// Process messages to keep from dropping circuit.
		LLMessageSystem* msg = gMessageSystem;
		while (msg->checkMessages(gFrameCount))
		{
		}
		msg->processAcks();
		LLUserAuth::UserAuthcode error = gUserAuthp->authResponse();
		if(LLUserAuth::E_NO_RESPONSE_YET == error)
		{
			//llinfos << "waiting..." << llendl;
			return do_normal_idle;
		}
		++gStartupState;
		progress += 0.01f;
		set_startup_status(progress, auth_desc.c_str(), auth_message.c_str());
		return do_normal_idle;
	}

	if(STATE_LOGIN_DOWNLOADING == gStartupState)
	{
		lldebugs << "STATE_LOGIN_DOWNLOADING" << llendl;
		if (!gUserAuthp)
		{
			llerrs << "No userauth in STATE_LOGIN_DOWNLOADING!" << llendl;
		}
		// Process messages to keep from dropping circuit.
		LLMessageSystem* msg = gMessageSystem;
		while (msg->checkMessages(gFrameCount))
		{
		}
		msg->processAcks();
		LLUserAuth::UserAuthcode error = gUserAuthp->authResponse();
		if(LLUserAuth::E_DOWNLOADING == error)
		{
			//llinfos << "downloading..." << llendl;
			return do_normal_idle;
		}
		++gStartupState;
		progress += 0.01f;
		set_startup_status(progress, "Processing Response...", auth_message.c_str());
		return do_normal_idle;
	}

	if(STATE_LOGIN_PROCESS_RESPONSE == gStartupState)
	{
		lldebugs << "STATE_LOGIN_PROCESS_RESPONSE" << llendl;
		std::ostringstream emsg;
		BOOL quit = FALSE;
		const char* login_response = NULL;
		const char* reason_response = NULL;
		const char* message_response = NULL;
		BOOL successful_login = FALSE;
		LLUserAuth::UserAuthcode error = gUserAuthp->authResponse();
		// reset globals
		gAcceptTOS = FALSE;
		gAcceptCriticalMessage = FALSE;
		switch(error)
		{
		case LLUserAuth::E_OK:
			login_response = gUserAuthp->getResponse("login");
			if(login_response && (0 == strcmp(login_response, "true")))
			{
				// Yay, login!
				successful_login = TRUE;
			}
			else if(login_response && (0 == strcmp(login_response, "indeterminate")))
			{
				llinfos << "Indeterminate login..." << llendl;
				auth_uri = gUserAuthp->getResponse("next_url");
				auth_method = gUserAuthp->getResponse("next_method");
				auth_message = gUserAuthp->getResponse("message");
				if(auth_method.substr(0, 5) == "login")
				{
					auth_desc.assign("Authenticating...");
				}
				else
				{
					auth_desc.assign("Performing account maintenance...");
				}
				// ignoring the duration & options array for now.
				// Go back to authenticate.
				gStartupState = STATE_LOGIN_AUTHENTICATE;
				return do_normal_idle;
			}
			else
			{
				emsg << "Login failed.\n";
				reason_response = gUserAuthp->getResponse("reason");
				message_response = gUserAuthp->getResponse("message");

				if (gHideLinks && reason_response && (0 == strcmp(reason_response, "disabled")))
				{
					emsg << gDisabledMessage;
				}
				else if (message_response)
				{
					emsg << message_response;
				}

				if(reason_response && (0 == strcmp(reason_response, "tos")))
				{
					if (show_connect_box)
					{
						llinfos << "Need tos agreement" << llendl;
						gStartupState = STATE_UPDATE_CHECK;
						LLFloaterTOS* tos_dialog = LLFloaterTOS::show(LLFloaterTOS::TOS_TOS,
																	message_response);
						tos_dialog->startModal();
						// LLFloaterTOS deletes itself.
						return FALSE;
					}
					else
					{
						quit = TRUE;
					}
				}
				if(reason_response && (0 == strcmp(reason_response, "critical")))
				{
					if (show_connect_box)
					{
						llinfos << "Need critical message" << llendl;
						gStartupState = STATE_UPDATE_CHECK;
						LLFloaterTOS* tos_dialog = LLFloaterTOS::show(LLFloaterTOS::TOS_CRITICAL_MESSAGE,
																	message_response);
						tos_dialog->startModal();
						// LLFloaterTOS deletes itself.
						return FALSE;
					}
					else
					{
						quit = TRUE;
					}
				}
				if(reason_response && (0 == strcmp(reason_response, "key")))
				{
					// Couldn't login because user/password is wrong
					// Clear the password
					password = "";
				}
				if(reason_response && (0 == strcmp(reason_response, "update")))
				{
					auth_message = gUserAuthp->getResponse("message");
					if (show_connect_box)
					{
						update_app(TRUE, auth_message);
						gStartupState = STATE_UPDATE_CHECK;
						return FALSE;
					}
					else
					{
						quit = TRUE;
					}
				}
				if(reason_response && (0 == strcmp(reason_response, "optional")))
				{
					llinfos << "Login got optional update" << llendl;
					auth_message = gUserAuthp->getResponse("message");
					if (show_connect_box)
					{
						update_app(FALSE, auth_message);
						gStartupState = STATE_UPDATE_CHECK;
						gSkipOptionalUpdate = TRUE;
						return FALSE;
					}
				}
			}
			break;
		case LLUserAuth::E_COULDNT_RESOLVE_HOST:
		case LLUserAuth::E_SSL_PEER_CERTIFICATE:
		case LLUserAuth::E_UNHANDLED_ERROR:
		default:
			emsg << "Unable to connect to " << gSecondLife << ".\n";
			emsg << gUserAuthp->errorMessage();
			break;
		case LLUserAuth::E_SSL_CACERT:
		case LLUserAuth::E_SSL_CONNECT_ERROR:
			emsg << "Unable to establish a secure connection to the login server.\n";
			emsg << gUserAuthp->errorMessage();
			break;
		}

		// Version update and we're not showing the dialog
		if(quit)
		{
			delete gUserAuthp;
			gUserAuthp = NULL;
			app_force_quit(NULL);
			return FALSE;
		}

		if(successful_login)
		{
		    if (!gUserAuthp)
		    {
			    llerrs << "No userauth on successful login!" << llendl;
		    }

			// unpack login data needed by the application
			const char* text;
			text = gUserAuthp->getResponse("agent_id");
			if(text) gAgentID.set(text);
			write_debug("AgentID: ");
			write_debug(text);
			write_debug("\n");
			
			text = gUserAuthp->getResponse("session_id");
			if(text) gAgentSessionID.set(text);
			write_debug("SessionID: ");
			write_debug(text);
			write_debug("\n");
			
			text = gUserAuthp->getResponse("secure_session_id");
			if(text) gAgent.mSecureSessionID.set(text);

			text = gUserAuthp->getResponse("first_name");
			if(text) 
			{
				// Remove quotes from string.  Login.cgi sends these to force
				// names that look like numbers into strings.
				firstname.assign(text);
				LLString::replaceChar(firstname, '"', ' ');
				LLString::trim(firstname);
			}
			text = gUserAuthp->getResponse("last_name");
			if(text) lastname.assign(text);
			gSavedSettings.setString("FirstName", firstname);
			gSavedSettings.setString("LastName", lastname);
			if (remember_password)
			{
				save_password_to_disk(password.c_str());
			}
			else
			{
				save_password_to_disk(NULL);
			}
			gSavedSettings.setBOOL("RememberPassword", remember_password);
			gSavedSettings.setBOOL("LoginLastLocation", gSavedSettings.getBOOL("LoginLastLocation"));
			gSavedSettings.setBOOL("LoggedIn", TRUE);

			text = gUserAuthp->getResponse("agent_access");
			if(text && (text[0] == 'M'))
			{
				gAgent.mAccess = SIM_ACCESS_MATURE;
			}
			else
			{
				gAgent.mAccess = SIM_ACCESS_PG;
			}

			text = gUserAuthp->getResponse("start_location");
			if(text) agent_start_location.assign(text);
			text = gUserAuthp->getResponse("circuit_code");
			if(text)
			{
				gMessageSystem->mOurCircuitCode = strtoul(text, NULL, 10);
			}
			const char* sim_ip_str = gUserAuthp->getResponse("sim_ip");
			const char* sim_port_str = gUserAuthp->getResponse("sim_port");
			if(sim_ip_str && sim_port_str)
			{
				U32 sim_port = strtoul(sim_port_str, NULL, 10);
				first_sim.set(sim_ip_str, sim_port);
				if (first_sim.isOk())
				{
					gMessageSystem->enableCircuit(first_sim, TRUE);
				}
			}
			const char* region_x_str = gUserAuthp->getResponse("region_x");
			const char* region_y_str = gUserAuthp->getResponse("region_y");
			if(region_x_str && region_y_str)
			{
				U32 region_x = strtoul(region_x_str, NULL, 10);
				U32 region_y = strtoul(region_y_str, NULL, 10);
				first_sim_handle = to_region_handle(region_x, region_y);
			}
			
			const char* look_at_str = gUserAuthp->getResponse("look_at");
			if (look_at_str)
			{
				LLMemoryStream mstr((U8*)look_at_str, strlen(look_at_str));
				LLSD sd = LLSDNotationParser::parse(mstr);
				agent_start_look_at = ll_vector3_from_sd(sd);
			}

			text = gUserAuthp->getResponse("seed_capability");
			if (text) first_sim_seed_cap = text;
						
			text = gUserAuthp->getResponse("seconds_since_epoch");
			if(text)
			{
				U32 server_utc_time = strtoul(text, NULL, 10);
				if(server_utc_time)
				{
					time_t now = time(NULL);
					gUTCOffset = (server_utc_time - now);
				}
			}

			const char* home_location = gUserAuthp->getResponse("home");
			if(home_location)
			{
				LLMemoryStream mstr((U8*)home_location, strlen(home_location));
				LLSD sd = LLSDNotationParser::parse(mstr);
				S32 region_x = sd["region_handle"][0].asInteger();
				S32 region_y = sd["region_handle"][1].asInteger();
				U64 region_handle = to_region_handle(region_x, region_y);
				LLVector3 position = ll_vector3_from_sd(sd["position"]);
				gAgent.setHomePosRegion(region_handle, position);
			}

			gAgent.mMOTD.assign(gUserAuthp->getResponse("message"));
			LLUserAuth::options_t options;
			if(gUserAuthp->getOptions("inventory-root", options))
			{
				LLUserAuth::response_t::iterator it;
				it = options[0].find("folder_id");
				if(it != options[0].end())
				{
					gAgent.mInventoryRootID.set((*it).second.c_str());
					//gInventory.mock(gAgent.getInventoryRootID());
				}
			}

			options.clear();
			if(gUserAuthp->getOptions("login-flags", options))
			{
				LLUserAuth::response_t::iterator it;
				LLUserAuth::response_t::iterator no_flag = options[0].end();
				it = options[0].find("ever_logged_in");
				if(it != no_flag)
				{
					if((*it).second == "N") gAgent.setFirstLogin(TRUE);
					else gAgent.setFirstLogin(FALSE);
				}
				it = options[0].find("stipend_since_login");
				if(it != no_flag)
				{
					if((*it).second == "Y") stipend_since_login = TRUE;
				}
				it = options[0].find("gendered");
				if(it != no_flag)
				{
					if((*it).second == "Y") gAgent.setGenderChosen(TRUE);
				}
				it = options[0].find("daylight_savings");
				if(it != no_flag)
				{
					if((*it).second == "Y")  gPacificDaylightTime = TRUE;
					else gPacificDaylightTime = FALSE;
				}
			}
			options.clear();
			if (gUserAuthp->getOptions("initial-outfit", options)
				&& !options.empty())
			{
				LLUserAuth::response_t::iterator it;
				LLUserAuth::response_t::iterator it_end = options[0].end();
				it = options[0].find("folder_name");
				if(it != it_end)
				{
					gInitialOutfit = (*it).second;
				}
				it = options[0].find("gender");
				if (it != it_end)
				{
					gInitialOutfitGender = (*it).second;
				}
			}

			options.clear();
			if(gUserAuthp->getOptions("global-textures", options))
			{
				// Extract sun and moon texture IDs.  These are used
				// in the LLVOSky constructor, but I can't figure out
				// how to pass them in.  JC
				LLUserAuth::response_t::iterator it;
				LLUserAuth::response_t::iterator no_texture = options[0].end();
				it = options[0].find("sun_texture_id");
				if(it != no_texture)
				{
					gSunTextureID.set((*it).second.c_str());
				}
				it = options[0].find("moon_texture_id");
				if(it != no_texture)
				{
					gMoonTextureID.set((*it).second.c_str());
				}
				it = options[0].find("cloud_texture_id");
				if(it != no_texture)
				{
					gCloudTextureID.set((*it).second.c_str());
				}
			}


			// JC: gesture loading done below, when we have an asset system
			// in place.  Don't delete/clear user_credentials until then.

			if(gAgentID.notNull()
			   && gAgentSessionID.notNull()
			   && gMessageSystem->mOurCircuitCode
			   && first_sim.isOk()
			   && gAgent.mInventoryRootID.notNull())
			{
				++gStartupState;
			}
			else
			{
				if (gNoRender)
				{
					llinfos << "Bad login - missing return values" << llendl;
					llinfos << emsg << llendl;
					exit(0);
				}
				// Bounce back to the login screen.
				LLStringBase<char>::format_map_t args;
				args["[ERROR_MESSAGE]"] = emsg.str();
				gViewerWindow->alertXml("ErrorMessage", args, login_alert_done);
				gStartupState = STATE_LOGIN_SHOW;
			}
		}
		else
		{
			if (gNoRender)
			{
				llinfos << "Failed to login!" << llendl;
				llinfos << emsg << llendl;
				exit(0);
			}
			// Bounce back to the login screen.
			LLStringBase<char>::format_map_t args;
			args["[ERROR_MESSAGE]"] = emsg.str();
			gViewerWindow->alertXml("ErrorMessage", args, login_alert_done);
			gStartupState = STATE_LOGIN_SHOW;
		}
		return do_normal_idle;
	}

	//---------------------------------------------------------------------
	// World Init
	//---------------------------------------------------------------------
	if (STATE_WORLD_INIT == gStartupState)
	{
		set_startup_status(0.40f, "Initializing World...", gAgent.mMOTD.c_str());
		display_startup();
		// We should have an agent id by this point.
		llassert(!(gAgentID == LLUUID::null));

		// Finish agent initialization.  (Requires gSavedSettings, builds camera)
		gAgent.init();

		// Since we connected, save off the settings so the user doesn't have to
		// type the name/password again if we crash.
		gSavedSettings.saveToFile(gSettingsFileName, TRUE);

		// Create selection manager
		// Must be done before menus created, because many enabled callbacks
		// require its existance.
		gSelectMgr = new LLSelectMgr();
		gParcelMgr = new LLViewerParcelMgr();
		gHUDManager = new LLHUDManager();
		gMuteListp = new LLMuteList();

		//
		// Initialize classes w/graphics stuff.
		//
		LLSurface::initClasses();

		LLFace::initClass();

		LLDrawable::initClass();

		// RN: don't initialize VO classes in drone mode, they are too closely tied to rendering
		LLViewerObject::initVOClasses();

		display_startup();

		// World initialization must be done after above window init
		gWorldp = new LLWorld(region_size, region_scale);

		// User might have overridden far clip
		gWorldp->setLandFarClip( gAgent.mDrawDistance );

		if (!gRunLocal)
		{
			// Before we create the first region, we need to set the agent's mOriginGlobal
			// This is necessary because creating objects before this is set will result in a
			// bad mPositionAgent cache.

			gAgent.initOriginGlobal(from_region_handle(first_sim_handle));

			gWorldp->addRegion(first_sim_handle, first_sim);

			LLViewerRegion *regionp = gWorldp->getRegionFromHandle(first_sim_handle);
			llinfos << "Adding initial simulator " << regionp->getOriginGlobal() << llendl;
			
			regionp->setSeedCapability(first_sim_seed_cap);
			
			// Set agent's initial region to be the one we just created.
			gAgent.setRegion(regionp);

			// Set agent's initial position, which will be read by LLVOAvatar when the avatar
			// object is created.  I think this must be done after setting the region.  JC
			gAgent.setPositionAgent(agent_start_position_region);
		}
		else
		{
			// With one simulator, assume region is at 0,0, hence has regionHandle 0
			// VEFFECT: Login
			gWorldp->addRegion(0, gAgentSimHost);
			gAgent.setRegion(gWorldp->getRegionFromHandle(0));
		}

		display_startup();

		// Initialize UI
		if (!gNoRender)
		{
			// Initialize all our tools.  Must be done after saved settings loaded.
			gToolMgr = new LLToolMgr();
			gToolMgr->initTools();
			// Quickly get something onscreen to look at.
			gViewerWindow->initWorldUI();

			// Move the progress view in front of the UI
			gViewerWindow->moveProgressViewToFront();

			gErrorStream.setFixedBuffer(gDebugView->mDebugConsolep);
			// set initial visibility of debug console
			gDebugView->mDebugConsolep->setVisible(gSavedSettings.getBOOL("ShowDebugConsole"));
			gDebugView->mStatViewp->setVisible(gSavedSettings.getBOOL("ShowDebugStats"));
		}

		//
		// Set message handlers
		//
		llinfos << "Initializing communications..." << llendl;

		// register callbacks for messages. . . do this after initial handshake to make sure that we don't catch any unwanted
		register_viewer_callbacks(gMessageSystem);

		// Debugging info parameters
		gMessageSystem->setMaxMessageTime( 0.5f );			// Spam if decoding all msgs takes more than 500 ms

		#ifndef	LL_RELEASE_FOR_DOWNLOAD
			gMessageSystem->setTimeDecodes( TRUE );				// Time the decode of each msg
			gMessageSystem->setTimeDecodesSpamThreshold( 0.05f );  // Spam if a single msg takes over 50ms to decode
		#endif

		gXferManager->registerCallbacks(gMessageSystem);

		gCacheName = new LLCacheName(gMessageSystem);
		gCacheName->addObserver(callback_cache_name);

		// Load stored cache if possible
		load_name_cache();

		// Data storage for map of world.
		gWorldMap = new LLWorldMap();

		// register null callbacks for audio until the audio system is initialized
		gMessageSystem->setHandlerFuncFast(_PREHASH_SoundTrigger, null_message_callback, NULL);
		gMessageSystem->setHandlerFuncFast(_PREHASH_AttachedSound, null_message_callback, NULL);

		//reset statistics
		gViewerStats->resetStats();

		if (!gNoRender)
		{
			//
			// Set up all of our statistics UI stuff.
			//
			init_stat_view();
		}

		display_startup();
		//
		// Set up region and surface defaults
		//


		// Sets up the parameters for the first simulator

		llinfos << "Initializing camera..." << llendl;
		gFrameTime    = totalTime();
		F32 last_time = gFrameTimeSeconds;
		gFrameTimeSeconds = (S64)(gFrameTime - gStartTime)/SEC_TO_MICROSEC;

		gFrameIntervalSeconds = gFrameTimeSeconds - last_time;
		if (gFrameIntervalSeconds < 0.f)
		{
			gFrameIntervalSeconds = 0.f;
		}

		// Make sure agent knows correct aspect ratio
		gCamera->setViewHeightInPixels(gViewerWindow->getWindowDisplayHeight());
		if (gViewerWindow->mWindow->getFullscreen())
		{
			gCamera->setAspect(gViewerWindow->getDisplayAspectRatio());
		}
		else
		{
			gCamera->setAspect( (F32) gViewerWindow->getWindowWidth() / (F32) gViewerWindow->getWindowHeight());
		}

		// Move agent to starting location. The position handed to us by
		// the space server is in global coordinates, but the agent frame
		// is in region local coordinates. Therefore, we need to adjust
		// the coordinates handed to us to fit in the local region.

		gAgent.setPositionAgent(agent_start_position_region);
		gAgent.resetAxes(agent_start_look_at);
		gAgent.stopCameraAnimation();
		gAgent.resetCamera();

		// Initialize global class data needed for surfaces (i.e. textures)
		if (!gNoRender)
		{
			llinfos << "Initializing sky..." << llendl;
			// Initialize all of the viewer object classes for the first time (doing things like texture fetches.
			gSky.init(initial_sun_direction);
		}

		set_startup_status(0.45f, "Decoding UI images...", gAgent.mMOTD.c_str());
		display_startup();
		llinfos << "Decoding images..." << llendl;
		// For all images pre-loaded into viewer cache, decode them.
		// Need to do this AFTER we init the sky
		gImageList.decodeAllImages();
		gStartupState++;

		// JC - Do this as late as possible to increase likelihood Purify
		// will run.
		if (!gRunLocal)
		{
			LLMessageSystem* msg = gMessageSystem;
			if (!msg->mOurCircuitCode)
			{
				llwarns << "Attempting to connect to simulator with a zero circuit code!" << llendl;
			}
			msg->enableCircuit(first_sim, TRUE);
			// now, use the circuit info to tell simulator about us!
			llinfos << "viewer: UserLoginLocationReply() Enabling " << first_sim << " with code " << msg->mOurCircuitCode << llendl;
			msg->newMessageFast(_PREHASH_UseCircuitCode);
			msg->nextBlockFast(_PREHASH_CircuitCode);
			msg->addU32Fast(_PREHASH_Code, msg->mOurCircuitCode);
			msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
			msg->addUUIDFast(_PREHASH_ID, gAgent.getID());
			msg->sendReliable(
				first_sim,
				MAX_TIMEOUT_COUNT,
				FALSE,
				TIMEOUT_SECONDS,
				use_circuit_callback,
				NULL);
		}

		timeout.reset();
		if (!gConnectToSomething)
		{
			gStartupState = STATE_MISC;
		}

		return do_normal_idle;
	}

	//---------------------------------------------------------------------
	// LLMediaEngine Init
	//---------------------------------------------------------------------
	if (STATE_QUICKTIME_INIT == gStartupState)
	{
		if (gViewerWindow)
		{
			if (gSavedSettings.getBOOL("MuteAudio"))
			{
				LLMediaEngine::updateClass( 0.0f );
			}
			else
			{
				LLMediaEngine::updateClass( gSavedSettings.getF32( "MediaAudioVolume" ) );
			}
		}

		#if LL_QUICKTIME_ENABLED	// windows only right now but will be ported to mac 
		if (!gQuickTimeInitialized)
		{
			// initialize quicktime libraries (fails gracefully if quicktime not installed ($QUICKTIME)
			llinfos << "Initializing QuickTime...." << llendl;
			set_startup_status(0.47f, "Initializing QuickTime...", gAgent.mMOTD.c_str());
			display_startup();
			#if LL_WINDOWS
				// Only necessary/available on Windows.
				if ( InitializeQTML ( 0L ) != noErr )
				{
					// quicktime init failed - turn off media engine support
					LLMediaEngine::getInstance ()->setAvailable ( FALSE );
					llinfos << "...not found - unable to initialize." << llendl;
					set_startup_status(0.47f, "QuickTime not found - unable to initialize.", gAgent.mMOTD.c_str());
				}
				else
				{
					llinfos << ".. initialized successfully." << llendl;
					set_startup_status(0.47f, "QuickTime initialized successfully.", gAgent.mMOTD.c_str());
				};
			#endif
			EnterMovies ();
			gQuickTimeInitialized = true;
		}
		#endif

		// Get list of URLs approved for usage
		// CP: removed since they're not useful without Mozilla enabled
		#if LL_MOZILLA_ENABLED
		LLUrlWhiteList::getInstance()->load();
		#endif

		// initialize mozilla if we're using web page on a prim or not using an external browser for floater
		BOOL use_web_pages_on_prims = gSavedSettings.getBOOL("UseWebPagesOnPrims");
		BOOL use_external_browser = gSavedSettings.getBOOL("UseExternalBrowser");

use_external_browser = false;

		if (use_web_pages_on_prims || !use_external_browser)
		{
			//llinfos << "Initializing web browser...." << llendl;
			//set_startup_status(0.48f, "Initializing web browser...", gAgent.mMOTD.c_str());
			//display_startup();
			// initialize mozilla
			LLString mozilla_path = gDirUtilp->getExecutableDir();
			mozilla_path.append( gDirUtilp->getDirDelimiter() );
#if LL_DEBUG
			mozilla_path.append( "mozilla_debug" );
#else
			mozilla_path.append( "mozilla" );
#endif

#if LL_MOZILLA_ENABLED
			if (!gMozillaInitialized)
			{
				void* platform_window = gViewerWindow->getPlatformWindow();
				mozilla_init_embedding(platform_window, mozilla_path);
			}
#endif

			if (use_web_pages_on_prims)
			{
				gMediaList = new LLViewerMediaList(2);
			}
		}

		gStartupState++;
		return do_normal_idle;
	}

	//---------------------------------------------------------------------
	// Agent Send
	//---------------------------------------------------------------------
	if(STATE_WORLD_WAIT == gStartupState)
	{
		//llinfos << "Waiting for simulator ack...." << llendl;
		set_startup_status(0.49f, "Waiting for region handshake...", gAgent.mMOTD.c_str());
		if(gGotUseCircuitCodeAck)
		{
			++gStartupState;
		}
		LLMessageSystem* msg = gMessageSystem;
		while (msg->checkMessages(gFrameCount))
		{
		}
		msg->processAcks();
		return do_normal_idle;
	}

	//---------------------------------------------------------------------
	// Agent Send
	//---------------------------------------------------------------------
	if (STATE_AGENT_SEND == gStartupState)
	{
		llinfos << "Connecting to region..." << llendl;
		set_startup_status(0.50f, "Connecting to region...", gAgent.mMOTD.c_str());
		// register with the message system so it knows we're
		// expecting this message
		LLMessageSystem* msg = gMessageSystem;
		msg->setHandlerFuncFast(
			_PREHASH_AgentMovementComplete,
			process_agent_movement_complete,
			NULL);
		LLViewerRegion* regionp = gAgent.getRegion();
		if(!gRunLocal && regionp)
		{
			send_complete_agent_movement(regionp->getHost());
			gAssetStorage->setUpstream(regionp->getHost());
			gCacheName->setUpstream(regionp->getHost());
			msg->newMessageFast(_PREHASH_EconomyDataRequest);
			gAgent.sendReliableMessage();
		}
		else
		{
			gStartupState++;
		}

		// Create login effect
		// But not on first login, because you can't see your avatar then
		if (!gAgent.isFirstLogin())
		{
			LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE);
			effectp->setPositionGlobal(gAgent.getPositionGlobal());
			effectp->setColor(LLColor4U(gAgent.getEffectColor()));
			gHUDManager->sendEffects();
		}

		gStartupState++;

		timeout.reset();
		return do_normal_idle;
	}

	//---------------------------------------------------------------------
	// Agent Wait
	//---------------------------------------------------------------------
	if (STATE_AGENT_WAIT == gStartupState)
	{
		LLMessageSystem* msg = gMessageSystem;
		while (msg->checkMessages(gFrameCount))
		{
			if (msg->isMessageFast(_PREHASH_AgentMovementComplete))
			{
				gStartupState++;
				// Sometimes we have more than one message in the
				// queue. break out of this loop and continue
				// processing. If we don't, then this could skip one
				// or more login steps.
				break;
			}
			else
			{
				//llinfos << "Awaiting AvatarInitComplete, got "
				//<< msg->getMessageName() << llendl;
			}
		}
		msg->processAcks();
		return do_normal_idle;
	}

	//---------------------------------------------------------------------
	// Inventory Send
	//---------------------------------------------------------------------
	if (STATE_INVENTORY_SEND == gStartupState)
	{
		if (!gUserAuthp)
		{
			llerrs << "No userauth in STATE_INVENTORY_SEND!" << llendl;
		}

		if (gConnectToSomething && !gRunLocal)
		{
			// unpack thin inventory
			LLUserAuth::options_t options;
			options.clear();
			//bool dump_buffer = false;
			
			if(gUserAuthp->getOptions("inventory-lib-root", options)
			   && !options.empty())
			{
				// should only be one
				LLUserAuth::response_t::iterator it;
				it = options[0].find("folder_id");
				if(it != options[0].end())
				{
					gInventoryLibraryRoot.set((*it).second.c_str());
				}
			}
 			options.clear();
			if(gUserAuthp->getOptions("inventory-lib-owner", options)
			   && !options.empty())
			{
				// should only be one
				LLUserAuth::response_t::iterator it;
				it = options[0].find("agent_id");
				if(it != options[0].end())
				{
					gInventoryLibraryOwner.set((*it).second.c_str());
				}
			}
 			options.clear();
 			if(gUserAuthp->getOptions("inventory-skel-lib", options)
			   && gInventoryLibraryOwner.notNull())
 			{
 				if(!gInventory.loadSkeleton(options, gInventoryLibraryOwner))
 				{
 					llwarns << "Problem loading inventory-skel-lib" << llendl;
 				}
 			}
 			options.clear();
 			if(gUserAuthp->getOptions("inventory-skeleton", options))
 			{
 				if(!gInventory.loadSkeleton(options, gAgent.getID()))
 				{
 					llwarns << "Problem loading inventory-skel-targets"
							<< llendl;
 				}
 			}

			options.clear();
 			if(gUserAuthp->getOptions("buddy-list", options))
 			{
				LLUserAuth::options_t::iterator it = options.begin();
				LLUserAuth::options_t::iterator end = options.end();
				LLAvatarTracker::buddy_map_t list;
				LLUUID agent_id;
				S32 has_rights = 0, given_rights = 0;
				for (; it != end; ++it)
				{
					LLUserAuth::response_t::const_iterator option_it;
					option_it = (*it).find("buddy_id");
					if(option_it != (*it).end())
					{
						agent_id.set((*option_it).second.c_str());
					}
					option_it = (*it).find("buddy_rights_has");
					if(option_it != (*it).end())
					{
						has_rights = atoi((*option_it).second.c_str());
					}
					option_it = (*it).find("buddy_rights_given");
					if(option_it != (*it).end())
					{
						given_rights = atoi((*option_it).second.c_str());
					}
					list[agent_id] = new LLRelationship(given_rights, has_rights, false);
				}
				LLAvatarTracker::instance().addBuddyList(list);
 			}

			options.clear();
 			if(gUserAuthp->getOptions("ui-config", options))
 			{
				LLUserAuth::options_t::iterator it = options.begin();
				LLUserAuth::options_t::iterator end = options.end();
				for (; it != end; ++it)
				{
					LLUserAuth::response_t::const_iterator option_it;
					option_it = (*it).find("allow_first_life");
					if(option_it != (*it).end())
					{
						if (option_it->second == "Y")
						{
							LLPanelAvatar::sAllowFirstLife = TRUE;
						}
					}
				}
 			}

			options.clear();
			if(gUserAuthp->getOptions("event_categories", options))
			{
				LLEventInfo::loadCategories(options);
			}
			if(gUserAuthp->getOptions("event_notifications", options))
			{
				gEventNotifier.load(options);
			}
			options.clear();
			if(gUserAuthp->getOptions("classified_categories", options))
			{
				LLClassifiedInfo::loadCategories(options);
			}
			gInventory.buildParentChildMap();
			gInventory.addChangedMask(LLInventoryObserver::ALL, LLUUID::null);
			gInventory.notifyObservers();

			// set up callbacks
			LLMessageSystem* msg = gMessageSystem;
			LLInventoryModel::registerCallbacks(msg);
			LLAvatarTracker::instance().registerCallbacks(msg);
			LLLandmark::registerCallbacks(msg);

			// request mute list
			gMuteListp->requestFromServer(gAgent.getID());

			// Get money and ownership credit information
			msg->newMessageFast(_PREHASH_MoneyBalanceRequest);
			msg->nextBlockFast(_PREHASH_AgentData);
			msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
			msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
			msg->nextBlockFast(_PREHASH_MoneyData);
			msg->addUUIDFast(_PREHASH_TransactionID, LLUUID::null );
			gAgent.sendReliableMessage();

			// request all group information
			// *FIX: This will not do the right thing if the message
			// gets there before the requestuserserverconnection
			// circuit is completed.
			gAgent.sendAgentDataUpdateRequest();


			// NOTE: removed as part of user-privacy
			// enhancements. this information should be available from
			// login. 2006-10-16 Phoenix.
			// get the users that have been granted modify powers
			//msg->newMessageFast(_PREHASH_RequestGrantedProxies);
			//msg->nextBlockFast(_PREHASH_AgentData);
			//msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
			//msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
			//gAgent.sendReliableMessage();

			BOOL shown_at_exit = gSavedSettings.getBOOL("ShowInventory");

			// Create the inventory views
			LLInventoryView::showAgentInventory();

			// Hide the inventory if it wasn't shown at exit
			if(!shown_at_exit)
			{
				LLInventoryView::toggleVisibility(NULL);
			}
		}
		gStartupState++;
		return do_normal_idle;
	}

	//---------------------------------------------------------------------
	// Assert agent to userserver
	//---------------------------------------------------------------------
	if (STATE_CONNECT_USERSERVER == gStartupState)
	{
		LLMessageSystem* msg = gMessageSystem;
		msg->enableCircuit(gUserServer, TRUE);
		msg->newMessage("ConnectAgentToUserserver");
		msg->nextBlockFast(_PREHASH_AgentData);
		msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
		msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
		msg->sendReliable(gUserServer);
		gStartupState++;
		return do_normal_idle;
	}

	//---------------------------------------------------------------------
	// Misc
	//---------------------------------------------------------------------
	if (STATE_MISC == gStartupState)
	{
		// Create a few objects if we don't actually have a world
		if (!gConnectToSomething)
		{
			// could add them here
		}

		// We have a region, and just did a big inventory download.
		// We can estimate the user's connection speed, and set their
		// max bandwidth accordingly.  JC
		if (gSavedSettings.getBOOL("FirstLoginThisInstall")
			&& gUserAuthp)
		{
			// This is actually a pessimistic computation, because TCP may not have enough
			// time to ramp up on the (small) default inventory file to truly measure max
			// bandwidth. JC
			F64 rate_bps = gUserAuthp->getLastTransferRateBPS();
			const F32 FAST_RATE_BPS = 600.f * 1024.f;
			const F32 FASTER_RATE_BPS = 750.f * 1024.f;
			F32 max_bandwidth = gViewerThrottle.getMaxBandwidth();
			if (rate_bps > FASTER_RATE_BPS
				&& rate_bps > max_bandwidth)
			{
				llinfos << "Fast network connection, increasing max bandwidth to " 
					<< FASTER_RATE_BPS/1024.f 
					<< " Kbps" << llendl;
				gViewerThrottle.setMaxBandwidth(FASTER_RATE_BPS / 1024.f);
			}
			else if (rate_bps > FAST_RATE_BPS
				&& rate_bps > max_bandwidth)
			{
				llinfos << "Fast network connection, increasing max bandwidth to " 
					<< FAST_RATE_BPS/1024.f 
					<< " Kbps" << llendl;
				gViewerThrottle.setMaxBandwidth(FAST_RATE_BPS / 1024.f);
			}
		}

		// We're successfully logged in.
		gSavedSettings.setBOOL("FirstLoginThisInstall", FALSE);


		// based on the comments, we've successfully logged in so we can delete the 'forced'
		// URL that the updater set in settings.ini (in a mostly paranoid fashion)
		LLString nextLoginLocation = gSavedSettings.getString( "NextLoginLocation" );
		if ( nextLoginLocation.length() )
		{
			// clear it
			gSavedSettings.setString( "NextLoginLocation", "" );

			// and make sure it's saved
			gSavedSettings.saveToFile( gSettingsFileName, TRUE );
		};

		if (!gNoRender)
		{
			// JC: Initializing audio requests many sounds for download.
			init_audio();

			// JC: Initialize "active" gestures.  This may also trigger
			// many gesture downloads, if this is the user's first
			// time on this machine or -purge has been run.
			LLUserAuth::options_t gesture_options;
			if (gUserAuthp->getOptions("gestures", gesture_options))
			{
				llinfos << "Gesture Manager loading " << gesture_options.size()
					<< llendl;
				std::vector<LLUUID> item_ids;
				LLUserAuth::options_t::iterator resp_it;
				for (resp_it = gesture_options.begin();
					 resp_it != gesture_options.end();
					 ++resp_it)
				{
					const LLUserAuth::response_t& response = *resp_it;
					LLUUID item_id;
					LLUUID asset_id;
					LLUserAuth::response_t::const_iterator option_it;

					option_it = response.find("item_id");
					if (option_it != response.end())
					{
						const std::string& uuid_string = (*option_it).second;
						item_id.set(uuid_string.c_str());
					}
					option_it = response.find("asset_id");
					if (option_it != response.end())
					{
						const std::string& uuid_string = (*option_it).second;
						asset_id.set(uuid_string.c_str());
					}

					if (item_id.notNull() && asset_id.notNull())
					{
						// Could schedule and delay these for later.
						const BOOL no_inform_server = FALSE;
						const BOOL no_deactivate_similar = FALSE;
						gGestureManager.activateGestureWithAsset(item_id, asset_id,
																 no_inform_server,
																 no_deactivate_similar);
						// We need to fetch the inventory items for these gestures
						// so we have the names to populate the UI.
						item_ids.push_back(item_id);
					}
				}

				LLGestureInventoryFetchObserver* fetch = new LLGestureInventoryFetchObserver();
				fetch->fetchItems(item_ids);
				// deletes itself when done
				gInventory.addObserver(fetch);
			}
		}
		gDisplaySwapBuffers = TRUE;

		LLMessageSystem* msg = gMessageSystem;
		msg->setHandlerFuncFast(_PREHASH_SoundTrigger,				process_sound_trigger);
		msg->setHandlerFuncFast(_PREHASH_PreloadSound,				process_preload_sound);
		msg->setHandlerFuncFast(_PREHASH_AttachedSound,				process_attached_sound);
		msg->setHandlerFuncFast(_PREHASH_AttachedSoundGainChange,	process_attached_sound_gain_change);
		//msg->setHandlerFuncFast(_PREHASH_AttachedSoundCutoffRadius,	process_attached_sound_cutoff_radius);
		msg->setHandlerFunc(
			"ConnectToUserserver",
			process_connect_to_userserver);

		llinfos << "Initialization complete" << llendl;
		gInitializationComplete = TRUE;

		gRenderStartTime.reset();

		// HACK: Inform simulator of window size.
		// Do this here so it's less likely to race with RegisterNewAgent.
		// TODO: Put this into RegisterNewAgent
		// JC - 7/20/2002
		gViewerWindow->sendShapeToSim();

		// if needed, show the money history window
		if (stipend_since_login && !gNoRender)
		{
			LLFloaterAccountHistory::show(NULL);
		}

		if (!gAgent.isFirstLogin())
		{
			bool url_ok = LLURLSimString::sInstance.parse();
			if (!((agent_start_location == "url" && url_ok) ||
                  (!url_ok && ((agent_start_location == "last" && gSavedSettings.getBOOL("LoginLastLocation")) ||
							   (agent_start_location == "home" && !gSavedSettings.getBOOL("LoginLastLocation"))))))
			{
				// The reason we show the alert is because we want to
				// reduce confusion for when you log in and your provided
				// location is not your expected location. So, if this is
				// your first login, then you do not have an expectation,
				// thus, do not show this alert.
				LLString::format_map_t args;
				if (url_ok)
				{
					args["[TYPE]"] = "desired";
					args["[HELP]"] = " ";
				}
				else if (gSavedSettings.getBOOL("LoginLastLocation"))
				{
					args["[TYPE]"] = "last";
					args["[HELP]"] = " \n ";
				}
				else
				{
					args["[TYPE]"] = "home";
					args["[HELP]"] = " \nYou may want to set a new home location.\n ";
				}
				gViewerWindow->alertXml("AvatarMoved", args);
			}
			else
			{
				if (samename)
				{
					// restore old camera pos
					gAgent.setFocusOnAvatar(FALSE, FALSE);
					gAgent.setCameraPosAndFocusGlobal(gSavedSettings.getVector3d("CameraPosOnLogout"), gSavedSettings.getVector3d("FocusPosOnLogout"), LLUUID::null);
					BOOL limit_hit = FALSE;
					gAgent.calcCameraPositionTargetGlobal(&limit_hit);
					if (limit_hit)
					{
						gAgent.setFocusOnAvatar(TRUE, FALSE);
					}
					gAgent.stopCameraAnimation();
				}
			}
		}

		gStartupState++;
		timeout.reset();
		return do_normal_idle;
	}

	if (STATE_PRECACHE == gStartupState)
	{
		do_normal_idle = TRUE;
		if (!did_precache)
		{
			did_precache = TRUE;
			// Don't preload map information!  The amount of data for all the
			// map items (icons for classifieds, avatar locations, etc.) is
			// huge, and not throttled.  This overflows the downstream
			// pipe during startup, when lots of information is being sent.
			// The problem manifests itself as invisible avatars on login. JC
			//gWorldMap->setCurrentLayer(0); // pre-load layer 0 of the world map

			gImageList.doPreloadImages(); // pre-load some images from static VFS
		}
		
		F32 timeout_frac = timeout.getElapsedTimeF32()/PRECACHING_DELAY;
		// wait precache-delay and for agent's avatar or a lot longer.
		if(((timeout_frac > 1.f) && gAgent.getAvatarObject())
		   || (timeout_frac > 3.f))
		{
			gStartupState++;
		}
		else
		{
			set_startup_status(0.50f + 0.50f * timeout_frac, "Precaching...",
							   gAgent.mMOTD.c_str());
		}

		return do_normal_idle;
	}

	if (STATE_WEARABLES_WAIT == gStartupState)
	{
		do_normal_idle = TRUE;

		static LLFrameTimer wearables_timer;

		const F32 wearables_time = wearables_timer.getElapsedTimeF32();
		const F32 MAX_WEARABLES_TIME = 10.f;

		if(gAgent.getWearablesLoaded() || !gAgent.isGenderChosen())
		{
			gStartupState++;
		}
		else if (wearables_time > MAX_WEARABLES_TIME)
		{
			gViewerWindow->alertXml("ClothingLoading");
			gViewerStats->incStat(LLViewerStats::ST_WEARABLES_TOO_LONG);
			gStartupState++;
		}
		else
		{
			set_startup_status(0.f + 0.25f * wearables_time / MAX_WEARABLES_TIME,
							 "Downloading clothing...",
							 gAgent.mMOTD.c_str());
		}
		return do_normal_idle;
	}

	if (STATE_CLEANUP == gStartupState)
	{
		set_startup_status(1.0, "", NULL);

		do_normal_idle = TRUE;

		// Let the map know about the inventory.
		if(gFloaterWorldMap)
		{
			gFloaterWorldMap->observeInventory(&gInventory);
			gFloaterWorldMap->observeFriends();
		}

		gViewerWindow->showCursor();
		gViewerWindow->getWindow()->resetBusyCount();
		gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW);
		//llinfos << "Done releasing bitmap" << llendl;
		gViewerWindow->setShowProgress(FALSE);
		gViewerWindow->setProgressCancelButtonVisible(FALSE, "");

		// We're not away from keyboard, even though login might have taken
		// a while. JC
		gAgent.clearAFK();

		// Have the agent start watching the friends list so we can update proxies
		gAgent.observeFriends();
		if (gSavedSettings.getBOOL("LoginAsGod"))
		{
			gAgent.requestEnterGodMode();
		}
		
		// On first start, ask user for gender
		dialog_choose_gender_first_start();

		// Start automatic replay if the flag is set.
		if (gSavedSettings.getBOOL("StatsAutoRun"))
		{
			LLUUID id;
			llinfos << "Starting automatic playback" << llendl;
			gAgentPilot.startPlayback();
		}

		// ok, if we've gotten this far and have a startup URL
		if (LLURLSimString::sInstance.parse())
		{
			// kick off request for landmark to startup URL
			if(gFloaterWorldMap)
			{
				LLVector3 pos = gAgent.getPositionAgent();
				if( LLURLSimString::sInstance.mSimName != gAgent.getRegion()->getName()
					|| LLURLSimString::sInstance.mX != llfloor(pos[VX])
					|| LLURLSimString::sInstance.mY != llfloor(pos[VY]) )
				{
					LLFloaterWorldMap::show(NULL, TRUE);
					gFloaterWorldMap->trackURL(LLURLSimString::sInstance.mSimName,
											   LLURLSimString::sInstance.mX,
											   LLURLSimString::sInstance.mY,
											   LLURLSimString::sInstance.mZ);
				}
			}
		}

		// Clean up the userauth stuff.
		if (gUserAuthp)
		{
			delete gUserAuthp;
			gUserAuthp = NULL;
		}

		gStartupState++;
		//RN: unmute audio now that we are entering world
		//JC: But only if the user wants audio working.
		if (gAudiop)
		{
			BOOL mute = gSavedSettings.getBOOL("MuteAudio");
			gAudiop->setMuted(mute);
		}

		// reset keyboard focus to sane state of pointing at world
		gFocusMgr.setKeyboardFocus(NULL, NULL);

#if 0 // sjb: enable for auto-enabling timer display 
		gDebugView->mFastTimerView->setVisible(TRUE);
#endif
		return do_normal_idle;
	}

	llwarns << "Reached end of idle_startup for state " << gStartupState << llendl;
	return do_normal_idle;
}

//
// local function definition
//

void unsupported_graphics_callback(S32 option, void* userdata)
{
	if (0 == option)
	{
		llinfos << "User cancelled after driver check" << llendl;
		std::string help_path;
		help_path = gDirUtilp->getExpandedFilename(LL_PATH_HELP,
			"unsupported_card.html");
		app_force_quit( help_path.c_str() );
	}

	LLPanelLogin::giveFocus();
}

void check_driver_callback(S32 option, void* userdata)
{
	if (0 == option)
	{
		llinfos << "User cancelled after driver check" << llendl;
		std::string help_path;
		help_path = gDirUtilp->getExpandedFilename(LL_PATH_HELP,
			"graphics_driver_update.html");
		app_force_quit( help_path.c_str() );
	}

	LLPanelLogin::giveFocus();
}

void login_show()
{
	LLPanelLogin::show(	gViewerWindow->getVirtualWindowRect(),
						gSavedSettings.getBOOL("UseDebugLogin"),
						login_callback, NULL );

	// Make sure all the UI textures are present and decoded.
	gImageList.decodeAllImages();

	if( USERSERVER_OTHER == gUserServerChoice )
	{
		LLPanelLogin::addServer( gUserServerName, USERSERVER_OTHER );
	}
	else
	{
		LLPanelLogin::addServer( gUserServerDomainName[gUserServerChoice].mLabel, gUserServerChoice );
	}

	// Arg!  We hate loops!
	LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_DMZ].mLabel,	USERSERVER_DMZ );
	LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_LOCAL].mLabel,	USERSERVER_LOCAL );
	LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_AGNI].mLabel,	USERSERVER_AGNI );
	LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_ADITI].mLabel,	USERSERVER_ADITI );
	LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_SIVA].mLabel,	USERSERVER_SIVA );
	LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_DURGA].mLabel,	USERSERVER_DURGA );
	LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_SHAKTI].mLabel,	USERSERVER_SHAKTI );
	LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_GANGA].mLabel,	USERSERVER_GANGA );
	LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_SOMA].mLabel,	USERSERVER_SOMA );
}

// Callback for when login screen is closed.  Option 0 = connect, option 1 = quit.
void login_callback(S32 option, void *userdata)
{
	const S32 CONNECT_OPTION = 0;
	const S32 QUIT_OPTION = 1;

	if (CONNECT_OPTION == option)
	{
		gStartupState++;
		return;
	}
	else if (QUIT_OPTION == option)
	{
		// Make sure we don't save the password if the user is trying to clear it.
		LLString first, last, password;
		BOOL remember = TRUE;
		LLPanelLogin::getFields(first, last, password, remember);
		if (!remember)
		{
			// turn off the setting and write out to disk
			gSavedSettings.setBOOL("RememberPassword", FALSE);
			gSavedSettings.saveToFile(gSettingsFileName, TRUE);

			// stomp the saved password on disk
			save_password_to_disk(NULL);
		}

		LLPanelLogin::close();

		// Next iteration through main loop should shut down the app cleanly.
		gQuit = TRUE;

		return;
	}
	else
	{
		llwarns << "Unknown login button clicked" << llendl;
	}
}

LLString load_password_from_disk()
{
	LLString hashed_password("");

	// Look for legacy "marker" password from settings.ini
	hashed_password = gSavedSettings.getString("Marker");
	if (!hashed_password.empty())
	{
		// Stomp the Marker entry.
		gSavedSettings.setString("Marker", "");

		// Return that password.
		return hashed_password;
	}

	std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,
													   "password.dat");
	FILE* fp = LLFile::fopen(filepath.c_str(), "rb");
	if (!fp)
	{
		return hashed_password;
	}

	// UUID is 16 bytes, written into ASCII is 32 characters
	// without trailing \0
	const S32 HASHED_LENGTH = 32;
	U8 buffer[HASHED_LENGTH+1];

	if (1 != fread(buffer, HASHED_LENGTH, 1, fp))
	{
		return hashed_password;
	}

	fclose(fp);

	// Decipher with MAC address
	LLXORCipher cipher(gMACAddress, 6);
	cipher.decrypt(buffer, HASHED_LENGTH);

	buffer[HASHED_LENGTH] = '\0';

	// Check to see if the mac address generated a bad hashed
	// password. It should be a hex-string or else the mac adress has
	// changed. This is a security feature to make sure that if you
	// get someone's password.dat file, you cannot hack their account.
	if(is_hex_string(buffer, HASHED_LENGTH))
	{
		hashed_password.assign((char*)buffer);
	}

	return hashed_password;
}

void save_password_to_disk(const char* hashed_password)
{
	std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,
													   "password.dat");
	if (!hashed_password)
	{
		// No password, remove the file.
		LLFile::remove(filepath.c_str());
	}
	else
	{
		FILE* fp = LLFile::fopen(filepath.c_str(), "wb");
		if (!fp)
		{
			return;
		}

		// Encipher with MAC address
		const S32 HASHED_LENGTH = 32;
		U8 buffer[HASHED_LENGTH+1];

		LLString::copy((char*)buffer, hashed_password, HASHED_LENGTH+1);

		LLXORCipher cipher(gMACAddress, 6);
		cipher.encrypt(buffer, HASHED_LENGTH);

		fwrite(buffer, HASHED_LENGTH, 1, fp);

		fclose(fp);
	}
}

BOOL is_hex_string(U8* str, S32 len)
{
	BOOL rv = TRUE;
	U8* c = str;
	while(rv && len--)
	{
		switch(*c)
		{
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
		case 'a':
		case 'b':
		case 'c':
		case 'd':
		case 'e':
		case 'f':
			++c;
			break;
		default:
			rv = FALSE;
			break;
		}
	}
	return rv;
}

void show_first_run_dialog()
{
	gViewerWindow->alertXml("FirstRun", first_run_dialog_callback, NULL);
}

void first_run_dialog_callback(S32 option, void* userdata)
{
	if (0 == option)
	{
		llinfos << "First run dialog cancelling" << llendl;
		LLWeb::loadURL( CREATE_ACCOUNT_URL );
	}

	LLPanelLogin::giveFocus();
}



void set_startup_status(const F32 frac, const char *string, const char* msg)
{
	gViewerWindow->setProgressPercent(frac*100);
	gViewerWindow->setProgressString(string);

	gViewerWindow->setProgressMessage(msg);
}

void on_userserver_name_resolved( BOOL success, const LLString& host_name, U32 ip, void* userdata )
{
	if( STATE_RESOLVING_USERSERVER != gStartupState )
	{
		llwarns << "Userserver name callback returned during invalid state!" << llendl;
		return;
	}

	if( success )
	{
		gUserServer.setAddress( ip );
		llinfos << "...Userserver resolved to " << gUserServer << llendl;
		gStartupState = STATE_USERSERVER_RESOLVED;
	}
	else
	{
		llwarns << "setHostByName failed" << llendl;
		
		LLStringBase<char>::format_map_t args;
		args["[HOST_NAME]"] = host_name;
		gViewerWindow->alertXml("SetByHostFail", args, login_alert_done );
		gStartupState = STATE_LOGIN_SHOW;
	}
}

void login_alert_status(S32 option, void* user_data)
{
	if (0 == option)
	{
		// OK button
	}
	else if (1 == option)
	{
		// Help button
		std::string help_path;
		help_path = gDirUtilp->getExpandedFilename(LL_PATH_HELP, "unable_to_connect.html");
		load_url_local_file(help_path.c_str() );
	}

	LLPanelLogin::giveFocus();
}

void update_app(BOOL mandatory, const std::string& auth_msg)
{
	// store off config state, as we might quit soon
	gSavedSettings.saveToFile(gSettingsFileName, TRUE);

	std::ostringstream message;

	//XUI:translate
	std::string msg;
	if (!auth_msg.empty())
	{
		msg = "(" + auth_msg + ") \n";
	}
	LLStringBase<char>::format_map_t args;
	args["[MESSAGE]"] = msg;
	
	BOOL *mandatoryp = new BOOL(mandatory);

#if LL_WINDOWS
	if (mandatory)
	{
		gViewerWindow->alertXml("DownloadWindowsMandatory", args,
								update_dialog_callback,
								(void *)mandatoryp);
	}
	else
	{
#if LL_RELEASE_FOR_DOWNLOAD
		gViewerWindow->alertXml("DownloadWindowsReleaseForDownload", args,
								update_dialog_callback,
								(void *)mandatoryp);
#else
		gViewerWindow->alertXml("DownloadWindows", args,
								update_dialog_callback,
								(void *)mandatoryp);
#endif
	}
#else
	if (mandatory)
	{
		gViewerWindow->alertXml("DownloadMacMandatory", args,
								update_dialog_callback,
								(void *)mandatoryp);
	}
	else
	{
#if LL_RELEASE_FOR_DOWNLOAD
		gViewerWindow->alertXml("DownloadMacReleaseForDownload", args,
								update_dialog_callback,
								(void *)mandatoryp);
#else
		gViewerWindow->alertXml("DownloadMac", args,
								update_dialog_callback,
								(void *)mandatoryp);
#endif
	}
#endif

}


void update_dialog_callback(S32 option, void *userdata)
{
	std::string update_exe_path;
	BOOL mandatory = *(BOOL *)userdata;

#if !LL_RELEASE_FOR_DOWNLOAD
	if (option == 2)
	{
		gStartupState++;
		return;
	}
#endif
	
	if (option == 1)
	{
		// ...user doesn't want to do it
		if (mandatory)
		{
			app_force_quit();
			// Bump them back to the login screen.
			//gStartupState = STATE_LOGIN_SHOW;
		}
		else
		{
			gStartupState++;
		}
		return;
	}

#if LL_WINDOWS
	char ip[MAX_STRING];

	update_exe_path = gDirUtilp->getTempFilename();
	if (update_exe_path.empty())
	{
		// We're hosed, bail
		llwarns << "LLDir::getTempFilename() failed" << llendl;
		app_force_quit(NULL);
		return;
	}

	update_exe_path += ".exe";

	std::string updater_source = gDirUtilp->getAppRODataDir();
	updater_source += gDirUtilp->getDirDelimiter();
	updater_source += "updater.exe";

	llinfos << "Calling CopyFile source: " << updater_source.c_str()
			<< " dest: " << update_exe_path
			<< llendl;


	if (!CopyFileA(updater_source.c_str(), update_exe_path.c_str(), FALSE))
	{
		llinfos << "Unable to copy the updater!" << llendl;
		app_force_quit(NULL);
		return;
	}
	u32_to_ip_string(gUserServer.getAddress(), ip);

	std::ostringstream params;
	params << "-userserver " << gUserServerName;

	// if a sim name was passed in via command line parameter (typically through a SLURL)
	if ( LLURLSimString::sInstance.mSimString.length() )
	{
		// record the location to start at next time
		gSavedSettings.setString( "NextLoginLocation", LLURLSimString::sInstance.mSimString ); 
	};

	if (gHideLinks)
	{
		// Figure out the program name.
		const char* data_dir = gDirUtilp->getAppRODataDir().c_str();
		// Roll back from the end, stopping at the first '\'
		const char* program_name = data_dir + strlen(data_dir);
		while ( (data_dir != --program_name) &&
				*(program_name) != '\\');
		
		if ( *(program_name) == '\\')
		{
			// We found a '\'.
			program_name++;
		}
		else
		{
			// Oops.
			program_name = "SecondLife";
		}

		params << " -silent -name \"" << gSecondLife << "\" -program \"" << program_name << "\"";
	}

	llinfos << "Calling updater: " << update_exe_path << " " << params.str() << llendl;

 	remove_marker_file(); // In case updater fails
	
	// Use spawn() to run asynchronously
	int retval = _spawnl(_P_NOWAIT, update_exe_path.c_str(), update_exe_path.c_str(), params.str().c_str(), NULL);
	llinfos << "Spawn returned " << retval << llendl;

#elif LL_DARWIN
	// if a sim name was passed in via command line parameter (typically through a SLURL)
	if ( LLURLSimString::sInstance.mSimString.length() )
	{
		// record the location to start at next time
		gSavedSettings.setString( "NextLoginLocation", LLURLSimString::sInstance.mSimString ); 
	};

	update_exe_path = "'";
	update_exe_path += gDirUtilp->getAppRODataDir();
	update_exe_path += "/AutoUpdater.app/Contents/MacOS/AutoUpdater' -userserver ";
	update_exe_path += gUserServerName;
	update_exe_path += " -name \"";
	update_exe_path += gSecondLife;
	update_exe_path += "\" &";

	llinfos << "Calling updater: " << update_exe_path << llendl;

 	remove_marker_file(); // In case updater fails
	
	// Run the auto-updater.
	system(update_exe_path.c_str());
	
#elif LL_LINUX
	OSMessageBox("Automatic updating is not yet implemented for Linux.\n"
		"Please download the latest version from www.secondlife.com.",
		NULL, OSMB_OK);
	remove_marker_file();
	
#endif
	app_force_quit(NULL);
}

void use_circuit_callback(void**, S32 result)
{
	// bail if we're quitting.
	if(gQuit) return;
	static bool called = false;
	if(!called)
	{
		called = true;
		if (result)
		{
			// Make sure user knows something bad happened. JC
			llinfos << "Backing up to login screen!" << llendl;
			gViewerWindow->alertXml("LoginPacketNeverReceived",
				login_alert_status, NULL);
			gStartupState = STATE_LOGIN_SHOW;
		}
		else
		{
			gGotUseCircuitCodeAck = true;
		}
	}
}

void register_viewer_callbacks(LLMessageSystem* msg)
{
	msg->setHandlerFuncFast(_PREHASH_LayerData,				process_layer_data );
	msg->setHandlerFuncFast(_PREHASH_ImageData,				LLViewerImage::receiveImage );
	msg->setHandlerFuncFast(_PREHASH_ImagePacket,				LLViewerImage::receiveImagePacket );
	msg->setHandlerFuncFast(_PREHASH_ObjectUpdate,				process_object_update );
	msg->setHandlerFunc("ObjectUpdateCompressed",				process_compressed_object_update );
	msg->setHandlerFunc("ObjectUpdateCached",					process_cached_object_update );
	msg->setHandlerFuncFast(_PREHASH_ImprovedTerseObjectUpdate, process_terse_object_update_improved );
	msg->setHandlerFunc("SimStats",				process_sim_stats);
	msg->setHandlerFuncFast(_PREHASH_HealthMessage,			process_health_message );
	msg->setHandlerFuncFast(_PREHASH_EconomyData,				process_economy_data);
	msg->setHandlerFunc("RegionInfo", LLViewerRegion::processRegionInfo);

	msg->setHandlerFuncFast(_PREHASH_ChatFromSimulator,		process_chat_from_simulator);
	msg->setHandlerFuncFast(_PREHASH_KillObject,				process_kill_object,	NULL);
	msg->setHandlerFuncFast(_PREHASH_SimulatorViewerTimeMessage,	process_time_synch,		NULL);
	msg->setHandlerFuncFast(_PREHASH_EnableSimulator,			process_enable_simulator);
	msg->setHandlerFuncFast(_PREHASH_DisableSimulator,			process_disable_simulator);
	msg->setHandlerFuncFast(_PREHASH_KickUser,					process_kick_user,		NULL);

	msg->setHandlerFunc("CrossedRegion", process_crossed_region);
	msg->setHandlerFuncFast(_PREHASH_TeleportFinish, process_teleport_finish);

	msg->setHandlerFuncFast(_PREHASH_AlertMessage,             process_alert_message);
	msg->setHandlerFunc("AgentAlertMessage", process_agent_alert_message);
	msg->setHandlerFuncFast(_PREHASH_MeanCollisionAlert,             process_mean_collision_alert_message,  NULL);
	msg->setHandlerFunc("ViewerFrozenMessage",             process_frozen_message);

	msg->setHandlerFuncFast(_PREHASH_RequestAvatarInfo,		process_avatar_info_request);
	msg->setHandlerFuncFast(_PREHASH_NameValuePair,			process_name_value);
	msg->setHandlerFuncFast(_PREHASH_RemoveNameValuePair,	process_remove_name_value);
	msg->setHandlerFuncFast(_PREHASH_AvatarAnimation,		process_avatar_animation);
	msg->setHandlerFuncFast(_PREHASH_AvatarAppearance,		process_avatar_appearance);
	msg->setHandlerFunc("AgentCachedTextureResponse",	LLAgent::processAgentCachedTextureResponse);
	msg->setHandlerFunc("RebakeAvatarTextures", LLVOAvatar::processRebakeAvatarTextures);
	msg->setHandlerFuncFast(_PREHASH_CameraConstraint,		process_camera_constraint);
	msg->setHandlerFuncFast(_PREHASH_AvatarSitResponse,		process_avatar_sit_response);
	msg->setHandlerFunc("SetFollowCamProperties",			process_set_follow_cam_properties);
	msg->setHandlerFunc("ClearFollowCamProperties",			process_clear_follow_cam_properties);

	msg->setHandlerFuncFast(_PREHASH_ImprovedInstantMessage,	process_improved_im);
	msg->setHandlerFuncFast(_PREHASH_ScriptQuestion,			process_script_question);
	msg->setHandlerFuncFast(_PREHASH_ObjectProperties,			LLSelectMgr::processObjectProperties, NULL);
	msg->setHandlerFuncFast(_PREHASH_ObjectPropertiesFamily,	LLSelectMgr::processObjectPropertiesFamily, NULL);
	msg->setHandlerFunc("ForceObjectSelect", LLSelectMgr::processForceObjectSelect);

	msg->setHandlerFuncFast(_PREHASH_MoneyBalanceReply,		process_money_balance_reply,	NULL);
	msg->setHandlerFuncFast(_PREHASH_CoarseLocationUpdate,		LLWorld::processCoarseUpdate, NULL);
	msg->setHandlerFuncFast(_PREHASH_ReplyTaskInventory, 		LLViewerObject::processTaskInv,	NULL);
	msg->setHandlerFuncFast(_PREHASH_DerezContainer,			process_derez_container, NULL);
	msg->setHandlerFuncFast(_PREHASH_ScriptRunningReply,
						&LLLiveLSLEditor::processScriptRunningReply);

	msg->setHandlerFuncFast(_PREHASH_DeRezAck, process_derez_ack);

	msg->setHandlerFunc("LogoutReply", process_logout_reply);

	//msg->setHandlerFuncFast(_PREHASH_AddModifyAbility,
	//					&LLAgent::processAddModifyAbility);
	//msg->setHandlerFuncFast(_PREHASH_RemoveModifyAbility,
	//					&LLAgent::processRemoveModifyAbility);
	msg->setHandlerFuncFast(_PREHASH_AgentDataUpdate,
						&LLAgent::processAgentDataUpdate);
	msg->setHandlerFuncFast(_PREHASH_AgentGroupDataUpdate,
						&LLAgent::processAgentGroupDataUpdate);
	msg->setHandlerFunc("AgentDropGroup",
						&LLAgent::processAgentDropGroup);
	// land ownership messages
	msg->setHandlerFuncFast(_PREHASH_ParcelOverlay,
						LLViewerParcelMgr::processParcelOverlay);
	msg->setHandlerFuncFast(_PREHASH_ParcelProperties,
						LLViewerParcelMgr::processParcelProperties);
	msg->setHandlerFunc("ParcelAccessListReply",
		LLViewerParcelMgr::processParcelAccessListReply);
	msg->setHandlerFunc("ParcelDwellReply",
		LLViewerParcelMgr::processParcelDwellReply);

	msg->setHandlerFunc("AvatarPropertiesReply",
						LLPanelAvatar::processAvatarPropertiesReply);
	msg->setHandlerFunc("AvatarInterestsReply",
						LLPanelAvatar::processAvatarInterestsReply);
	msg->setHandlerFunc("AvatarGroupsReply",
						LLPanelAvatar::processAvatarGroupsReply);
	msg->setHandlerFuncFast(_PREHASH_AvatarStatisticsReply,
						LLPanelAvatar::processAvatarStatisticsReply);
	msg->setHandlerFunc("AvatarNotesReply",
						LLPanelAvatar::processAvatarNotesReply);
	msg->setHandlerFunc("AvatarPicksReply",
						LLPanelAvatar::processAvatarPicksReply);
	msg->setHandlerFunc("AvatarClassifiedReply",
						LLPanelAvatar::processAvatarClassifiedReply);

	msg->setHandlerFuncFast(_PREHASH_CreateGroupReply,
						LLGroupMgr::processCreateGroupReply);
	msg->setHandlerFuncFast(_PREHASH_JoinGroupReply,
						LLGroupMgr::processJoinGroupReply);
	msg->setHandlerFuncFast(_PREHASH_EjectGroupMemberReply,
						LLGroupMgr::processEjectGroupMemberReply);
	msg->setHandlerFuncFast(_PREHASH_LeaveGroupReply,
						LLGroupMgr::processLeaveGroupReply);
	msg->setHandlerFuncFast(_PREHASH_GroupProfileReply,
						LLGroupMgr::processGroupPropertiesReply);

	msg->setHandlerFuncFast(_PREHASH_ReputationIndividualReply,
						LLFloaterRate::processReputationIndividualReply);

	msg->setHandlerFuncFast(_PREHASH_AgentWearablesUpdate,
						LLAgent::processAgentInitialWearablesUpdate );

	msg->setHandlerFunc("ScriptControlChange",
						LLAgent::processScriptControlChange );

	msg->setHandlerFuncFast(_PREHASH_GestureUpdate,
						LLViewerGestureList::processGestureUpdate);

	msg->setHandlerFuncFast(_PREHASH_ViewerEffect, LLHUDManager::processViewerEffect);

	msg->setHandlerFuncFast(_PREHASH_GrantGodlikePowers, process_grant_godlike_powers);

	msg->setHandlerFuncFast(_PREHASH_MoneySummaryReply,
						LLFloaterAccountHistory::processMoneySummaryReply);
	msg->setHandlerFuncFast(_PREHASH_MoneyDetailsReply,
						LLFloaterAccountHistory::processMoneyDetailsReply);
	msg->setHandlerFuncFast(_PREHASH_MoneyTransactionsReply,
						LLFloaterAccountHistory::processMoneyTransactionsReply);

	// ASDF
	msg->setHandlerFuncFast(_PREHASH_GroupAccountSummaryReply,
							LLGroupMoneyPlanningTabEventHandler::processGroupAccountSummaryReply);
	msg->setHandlerFuncFast(_PREHASH_GroupAccountDetailsReply,
							LLGroupMoneyDetailsTabEventHandler::processGroupAccountDetailsReply);
	msg->setHandlerFuncFast(_PREHASH_GroupAccountTransactionsReply,
							LLGroupMoneySalesTabEventHandler::processGroupAccountTransactionsReply);

	msg->setHandlerFuncFast(_PREHASH_UserInfoReply,
		process_user_info_reply);

	msg->setHandlerFunc("RegionHandshake", process_region_handshake, NULL);

	msg->setHandlerFunc("TeleportStart", process_teleport_start );
	msg->setHandlerFunc("TeleportProgress", process_teleport_progress);
	msg->setHandlerFunc("TeleportFailed", process_teleport_failed, NULL);
	msg->setHandlerFunc("TeleportLocal", process_teleport_local, NULL);

	msg->setHandlerFunc("ImageNotInDatabase", LLViewerImageList::processImageNotInDatabase, NULL);

	msg->setHandlerFuncFast(_PREHASH_GroupMembersReply,
						LLGroupMgr::processGroupMembersReply);
	msg->setHandlerFunc("GroupRoleDataReply",
						LLGroupMgr::processGroupRoleDataReply);
	msg->setHandlerFunc("GroupRoleMembersReply",
						LLGroupMgr::processGroupRoleMembersReply);
	msg->setHandlerFunc("GroupTitlesReply",
						LLGroupMgr::processGroupTitlesReply);
	// Special handler as this message is sometimes used for group land.
	msg->setHandlerFunc("PlacesReply", process_places_reply);
	msg->setHandlerFunc("GroupNoticesListReply", LLPanelGroupNotices::processGroupNoticesListReply);

	msg->setHandlerFunc("DirPlacesReply", LLPanelDirBrowser::processDirPlacesReply);
	msg->setHandlerFunc("DirPeopleReply", LLPanelDirBrowser::processDirPeopleReply);
	msg->setHandlerFunc("DirEventsReply", LLPanelDirBrowser::processDirEventsReply);
	msg->setHandlerFunc("DirGroupsReply", LLPanelDirBrowser::processDirGroupsReply);
	//msg->setHandlerFunc("DirPicksReply",  LLPanelDirBrowser::processDirPicksReply);
	msg->setHandlerFunc("DirClassifiedReply",  LLPanelDirBrowser::processDirClassifiedReply);
	msg->setHandlerFunc("DirLandReply",   LLPanelDirBrowser::processDirLandReply);
	msg->setHandlerFunc("DirPopularReply",LLPanelDirBrowser::processDirPopularReply);

	msg->setHandlerFunc("AvatarPickerReply", LLFloaterAvatarPicker::processAvatarPickerReply);

	msg->setHandlerFunc("MapLayerReply", LLWorldMap::processMapLayerReply);
	msg->setHandlerFunc("MapBlockReply", LLWorldMap::processMapBlockReply);
	msg->setHandlerFunc("MapItemReply", LLWorldMap::processMapItemReply);

	msg->setHandlerFunc("EventInfoReply", LLPanelEvent::processEventInfoReply);
	msg->setHandlerFunc("PickInfoReply", LLPanelPick::processPickInfoReply);
	msg->setHandlerFunc("ClassifiedInfoReply", LLPanelClassified::processClassifiedInfoReply);
	msg->setHandlerFunc("ParcelInfoReply", LLPanelPlace::processParcelInfoReply);
	msg->setHandlerFunc("ScriptDialog", process_script_dialog);
	msg->setHandlerFunc("LoadURL", process_load_url);
	msg->setHandlerFunc("ScriptTeleportRequest", process_script_teleport_request);
	msg->setHandlerFunc("EstateCovenantReply", process_covenant_reply);

	// calling cards
	msg->setHandlerFunc("OfferCallingCard", process_offer_callingcard);
	msg->setHandlerFunc("AcceptCallingCard", process_accept_callingcard);
	msg->setHandlerFunc("DeclineCallingCard", process_decline_callingcard);

	msg->setHandlerFunc("ParcelObjectOwnersReply", LLPanelLandObjects::processParcelObjectOwnersReply);

	// Reponse to the "Refresh" button on land objects floater.
	if (gSavedSettings.getBOOL("AudioStreamingVideo"))
	{
		msg->setHandlerFunc("ParcelMediaCommandMessage", LLMediaEngine::process_parcel_media);
		msg->setHandlerFunc ( "ParcelMediaUpdate", LLMediaEngine::process_parcel_media_update );
	}
	else
	{
		msg->setHandlerFunc("ParcelMediaCommandMessage", null_message_callback);
		gMessageSystem->setHandlerFunc ( "ParcelMediaUpdate", null_message_callback );
	}

	msg->setHandlerFunc("InitiateDownload", process_initiate_download);
	msg->setHandlerFunc("LandStatReply", LLFloaterTopObjects::handle_land_reply);
	msg->setHandlerFunc("GenericMessage", process_generic_message);

	msg->setHandlerFuncFast(_PREHASH_FeatureDisabled, process_feature_disabled_message);
}


void init_stat_view()
{
	LLFrameStatView *frameviewp = gDebugView->mFrameStatView;
	frameviewp->setup(gFrameStats);
	frameviewp->mShowPercent = FALSE;

	LLRect rect;
	LLStatBar *stat_barp;
	rect = gDebugView->mStatViewp->getRect();

	//
	// Viewer advanced stats
	//
	LLStatView *stat_viewp = NULL;

	//
	// Viewer Basic
	//
	stat_viewp = new LLStatView("basic stat view", "Basic",	"OpenDebugStatBasic", rect);
	gDebugView->mStatViewp->addChildAtEnd(stat_viewp);

	stat_barp = stat_viewp->addStat("FPS", &(gViewerStats->mFPSStat));
	stat_barp->setUnitLabel(" fps");
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 45.f;
	stat_barp->mTickSpacing = 7.5f;
	stat_barp->mLabelSpacing = 15.f;
	stat_barp->mPrecision = 1;
	stat_barp->mDisplayBar = TRUE;
	stat_barp->mDisplayHistory = TRUE;

	stat_barp = stat_viewp->addStat("Bandwidth", &(gViewerStats->mKBitStat));
	stat_barp->setUnitLabel(" kbps");
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 900.f;
	stat_barp->mTickSpacing = 100.f;
	stat_barp->mLabelSpacing = 300.f;
	stat_barp->mDisplayBar = TRUE;
	stat_barp->mDisplayHistory = FALSE;

	stat_barp = stat_viewp->addStat("Packet Loss", &(gViewerStats->mPacketsLostPercentStat));
	stat_barp->setUnitLabel(" %%");
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 5.f;
	stat_barp->mTickSpacing = 1.f;
	stat_barp->mLabelSpacing = 1.f;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayMean = TRUE;
	stat_barp->mPrecision = 1;

	stat_barp = stat_viewp->addStat("Ping Sim", &(gViewerStats->mSimPingStat));
	stat_barp->setUnitLabel(" msec");
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 1000.f;
	stat_barp->mTickSpacing = 100.f;
	stat_barp->mLabelSpacing = 200.f;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayMean = FALSE;

	stat_barp = stat_viewp->addStat("Ping User", &(gViewerStats->mUserserverPingStat));
	stat_barp->setUnitLabel(" msec");
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 1000.f;
	stat_barp->mTickSpacing = 100.f;
	stat_barp->mLabelSpacing = 200.f;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayMean = FALSE;


	stat_viewp = new LLStatView("advanced stat view", "Advanced", "OpenDebugStatAdvanced", rect);
	gDebugView->mStatViewp->addChildAtEnd(stat_viewp);


	LLStatView *render_statviewp;
	render_statviewp = new LLStatView("render stat view", "Render", "OpenDebugStatRender", rect);
	stat_viewp->addChildAtEnd(render_statviewp);

	stat_barp = render_statviewp->addStat("KTris Drawn", &(gPipeline.mTrianglesDrawnStat));
	stat_barp->setUnitLabel("/fr");
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 500.f;
	stat_barp->mTickSpacing = 100.f;
	stat_barp->mLabelSpacing = 500.f;
	stat_barp->mPrecision = 1;
	stat_barp->mPerSec = FALSE;

	stat_barp = render_statviewp->addStat("KTris Drawn", &(gPipeline.mTrianglesDrawnStat));
	stat_barp->setUnitLabel("/sec");
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 3000.f;
	stat_barp->mTickSpacing = 250.f;
	stat_barp->mLabelSpacing = 1000.f;
	stat_barp->mPrecision = 1;

	stat_barp = render_statviewp->addStat("Total Objs", &(gObjectList.mNumObjectsStat));
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 10000.f;
	stat_barp->mTickSpacing = 2500.f;
	stat_barp->mLabelSpacing = 5000.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;

	stat_barp = render_statviewp->addStat("New Objs", &(gObjectList.mNumNewObjectsStat));
	stat_barp->setLabel("New Objs");
	stat_barp->setUnitLabel("/sec");
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 1000.f;
	stat_barp->mTickSpacing = 100.f;
	stat_barp->mLabelSpacing = 500.f;
	stat_barp->mPerSec = TRUE;
	stat_barp->mDisplayBar = FALSE;


	// Pipeline statistics
	LLStatView *pipeline_statviewp;
	pipeline_statviewp = new LLStatView("pipeline stat view", "Pipeline", "", rect);
	render_statviewp->addChildAtEnd(pipeline_statviewp);

	stat_barp = pipeline_statviewp->addStat("Visible Drawables", &(gPipeline.mNumVisibleDrawablesStat));
	stat_barp->setUnitLabel("");
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 10000.f;
	stat_barp->mTickSpacing = 1000.f;
	stat_barp->mLabelSpacing = 5000.f;
	stat_barp->mPerSec = FALSE;

	stat_barp = pipeline_statviewp->addStat("Visible Faces", &(gPipeline.mNumVisibleFacesStat));
	stat_barp->setUnitLabel("");
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 40000.f;
	stat_barp->mTickSpacing = 5000.f;
	stat_barp->mLabelSpacing = 10000.f;
	stat_barp->mPerSec = FALSE;

	stat_barp = pipeline_statviewp->addStat("DirtyGeom", &(gPipeline.mGeometryChangesStat));
	stat_barp->setUnitLabel("/fr");
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 1000.f;
	stat_barp->mTickSpacing = 100.f;
	stat_barp->mLabelSpacing = 500.f;
	stat_barp->mPerSec = FALSE;

	stat_barp = pipeline_statviewp->addStat("DirtyLight", &(gPipeline.mLightingChangesStat));
	stat_barp->setUnitLabel("/fr");
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 1000.f;
	stat_barp->mTickSpacing = 100.f;
	stat_barp->mLabelSpacing = 500.f;
	stat_barp->mPerSec = FALSE;

	stat_barp = pipeline_statviewp->addStat("MoveList", &(gPipeline.mMoveChangesStat));
	stat_barp->setUnitLabel("dr");
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 1000.f;
	stat_barp->mTickSpacing = 100.f;
	stat_barp->mLabelSpacing = 500.f;
	stat_barp->mPerSec = FALSE;

	stat_barp = pipeline_statviewp->addStat("Compiles", &(gPipeline.mCompilesStat));
	stat_barp->setUnitLabel("/fr");
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 1000.f;
	stat_barp->mTickSpacing = 100.f;
	stat_barp->mLabelSpacing = 500.f;
	stat_barp->mPerSec = FALSE;

	stat_barp = pipeline_statviewp->addStat("Verts Relit", &(gPipeline.mVerticesRelitStat));
	stat_barp->setUnitLabel("/fr");
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 40000.f;
	stat_barp->mTickSpacing = 10000.f;
	stat_barp->mLabelSpacing = 20000.f;
	stat_barp->mPerSec = FALSE;

	// Texture statistics
	LLStatView *texture_statviewp;
	texture_statviewp = new LLStatView("texture stat view", "Texture", "", rect);
	render_statviewp->addChildAtEnd(texture_statviewp);

	stat_barp = texture_statviewp->addStat("Count", &(gImageList.sNumImagesStat));
	stat_barp->setUnitLabel("");
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 8000.f;
	stat_barp->mTickSpacing = 2000.f;
	stat_barp->mLabelSpacing = 4000.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;

	stat_barp = texture_statviewp->addStat("Raw Count", &(gImageList.sNumRawImagesStat));
	stat_barp->setUnitLabel("");
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 8000.f;
	stat_barp->mTickSpacing = 2000.f;
	stat_barp->mLabelSpacing = 4000.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;

	stat_barp = texture_statviewp->addStat("GL Mem", &(gImageList.sGLTexMemStat));
	stat_barp->setUnitLabel("");
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 400.f;
	stat_barp->mTickSpacing = 100.f;
	stat_barp->mLabelSpacing = 200.f;
	stat_barp->mPrecision = 1;
	stat_barp->mPerSec = FALSE;

	stat_barp = texture_statviewp->addStat("Formatted Mem", &(gImageList.sFormattedMemStat));
	stat_barp->setUnitLabel("");
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 400.f;
	stat_barp->mTickSpacing = 100.f;
	stat_barp->mLabelSpacing = 200.f;
	stat_barp->mPrecision = 1;
	stat_barp->mPerSec = FALSE;

	stat_barp = texture_statviewp->addStat("Raw Mem", &(gImageList.sRawMemStat));
	stat_barp->setUnitLabel("");
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 400.f;
	stat_barp->mTickSpacing = 100.f;
	stat_barp->mLabelSpacing = 200.f;
	stat_barp->mPrecision = 1;
	stat_barp->mPerSec = FALSE;

	stat_barp = texture_statviewp->addStat("Bound Mem", &(gImageList.sGLBoundMemStat));
	stat_barp->setUnitLabel("");
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 400.f;
	stat_barp->mTickSpacing = 100.f;
	stat_barp->mLabelSpacing = 200.f;
	stat_barp->mPrecision = 1;
	stat_barp->mPerSec = FALSE;

	
	// Network statistics
	LLStatView *net_statviewp;
	net_statviewp = new LLStatView("network stat view", "Network", "OpenDebugStatNet", rect);
	stat_viewp->addChildAtEnd(net_statviewp);

	stat_barp = net_statviewp->addStat("Packets In", &(gViewerStats->mPacketsInStat));
	stat_barp->setUnitLabel("/sec");
	stat_barp->mDisplayBar = FALSE;

	stat_barp = net_statviewp->addStat("Packets Out", &(gViewerStats->mPacketsOutStat));
	stat_barp->setUnitLabel("/sec");
	stat_barp->mDisplayBar = FALSE;

	stat_barp = net_statviewp->addStat("Objects", &(gViewerStats->mObjectKBitStat));
	stat_barp->setUnitLabel(" kbps");
	stat_barp->mDisplayBar = FALSE;

	stat_barp = net_statviewp->addStat("Texture", &(gViewerStats->mTextureKBitStat));
	stat_barp->setUnitLabel(" kbps");
	stat_barp->mDisplayBar = FALSE;

	stat_barp = net_statviewp->addStat("Asset", &(gViewerStats->mAssetKBitStat));
	stat_barp->setUnitLabel(" kbps");
	stat_barp->mDisplayBar = FALSE;

	stat_barp = net_statviewp->addStat("Layers", &(gViewerStats->mLayersKBitStat));
	stat_barp->setUnitLabel(" kbps");
	stat_barp->mDisplayBar = FALSE;

	stat_barp = net_statviewp->addStat("Actual In", &(gViewerStats->mActualInKBitStat));
	stat_barp->setUnitLabel(" kbps");
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 1024.f;
	stat_barp->mTickSpacing = 128.f;
	stat_barp->mLabelSpacing = 256.f;
	stat_barp->mDisplayBar = TRUE;
	stat_barp->mDisplayHistory = FALSE;

	stat_barp = net_statviewp->addStat("Actual Out", &(gViewerStats->mActualOutKBitStat));
	stat_barp->setUnitLabel(" kbps");
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 512.f;
	stat_barp->mTickSpacing = 128.f;
	stat_barp->mLabelSpacing = 256.f;
	stat_barp->mDisplayBar = TRUE;
	stat_barp->mDisplayHistory = FALSE;

	stat_barp = net_statviewp->addStat("VFS Pending Ops", &(gViewerStats->mVFSPendingOperations));
	stat_barp->setUnitLabel(" ");
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;


	// Simulator stats
	LLStatView *sim_statviewp = new LLStatView("sim stat view", "Simulator", "OpenDebugStatSim", rect);
	gDebugView->mStatViewp->addChildAtEnd(sim_statviewp);

	stat_barp = sim_statviewp->addStat("Time Dilation", &(gViewerStats->mSimTimeDilation));
	stat_barp->mPrecision = 2;
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 1.f;
	stat_barp->mTickSpacing = 0.25f;
	stat_barp->mLabelSpacing = 0.5f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mDisplayMean = FALSE;

	stat_barp = sim_statviewp->addStat("Sim FPS", &(gViewerStats->mSimFPS));
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 200.f;
	stat_barp->mTickSpacing = 20.f;
	stat_barp->mLabelSpacing = 100.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mDisplayMean = FALSE;

	stat_barp = sim_statviewp->addStat("Physics FPS", &(gViewerStats->mSimPhysicsFPS));
	stat_barp->mPrecision = 1;
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 66.f;
	stat_barp->mTickSpacing = 33.f;
	stat_barp->mLabelSpacing = 33.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mDisplayMean = FALSE;

	stat_barp = sim_statviewp->addStat("Agent Updates/Sec", &(gViewerStats->mSimAgentUPS));
	stat_barp->mPrecision = 1;
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 100.f;
	stat_barp->mTickSpacing = 25.f;
	stat_barp->mLabelSpacing = 50.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mDisplayMean = FALSE;

	stat_barp = sim_statviewp->addStat("Main Agents", &(gViewerStats->mSimMainAgents));
	stat_barp->mPrecision = 0;
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 80.f;
	stat_barp->mTickSpacing = 10.f;
	stat_barp->mLabelSpacing = 40.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mDisplayMean = FALSE;

	stat_barp = sim_statviewp->addStat("Child Agents", &(gViewerStats->mSimChildAgents));
	stat_barp->mPrecision = 0;
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 40.f;
	stat_barp->mTickSpacing = 5.f;
	stat_barp->mLabelSpacing = 10.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mDisplayMean = FALSE;

	stat_barp = sim_statviewp->addStat("Objects", &(gViewerStats->mSimObjects));
	stat_barp->mPrecision = 0;
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 30000.f;
	stat_barp->mTickSpacing = 5000.f;
	stat_barp->mLabelSpacing = 10000.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mDisplayMean = FALSE;

	stat_barp = sim_statviewp->addStat("Active Objects", &(gViewerStats->mSimActiveObjects));
	stat_barp->mPrecision = 0;
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 800.f;
	stat_barp->mTickSpacing = 100.f;
	stat_barp->mLabelSpacing = 200.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mDisplayMean = FALSE;

	stat_barp = sim_statviewp->addStat("Active Scripts", &(gViewerStats->mSimActiveScripts));
	stat_barp->mPrecision = 0;
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 800.f;
	stat_barp->mTickSpacing = 100.f;
	stat_barp->mLabelSpacing = 200.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mDisplayMean = FALSE;

	stat_barp = sim_statviewp->addStat("Script Perf", &(gViewerStats->mSimLSLIPS));
	stat_barp->setUnitLabel(" ips");
	stat_barp->mPrecision = 0;
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 100000.f;
	stat_barp->mTickSpacing = 25000.f;
	stat_barp->mLabelSpacing = 50000.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mDisplayMean = FALSE;

	stat_barp = sim_statviewp->addStat("Packets In", &(gViewerStats->mSimInPPS));
	stat_barp->setUnitLabel(" pps");
	stat_barp->mPrecision = 0;
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 2000.f;
	stat_barp->mTickSpacing = 250.f;
	stat_barp->mLabelSpacing = 1000.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mDisplayMean = FALSE;

	stat_barp = sim_statviewp->addStat("Packets Out", &(gViewerStats->mSimOutPPS));
	stat_barp->setUnitLabel(" pps");
	stat_barp->mPrecision = 0;
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 2000.f;
	stat_barp->mTickSpacing = 250.f;
	stat_barp->mLabelSpacing = 1000.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mDisplayMean = FALSE;

	stat_barp = sim_statviewp->addStat("Pending Downloads", &(gViewerStats->mSimPendingDownloads));
	stat_barp->mPrecision = 0;
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 800.f;
	stat_barp->mTickSpacing = 100.f;
	stat_barp->mLabelSpacing = 200.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mDisplayMean = FALSE;

	stat_barp = sim_statviewp->addStat("Pending Uploads", &(gViewerStats->mSimPendingUploads));
	stat_barp->mPrecision = 0;
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 100.f;
	stat_barp->mTickSpacing = 25.f;
	stat_barp->mLabelSpacing = 50.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mDisplayMean = FALSE;

	stat_barp = sim_statviewp->addStat("Total Unacked Bytes", &(gViewerStats->mSimTotalUnackedBytes));
	stat_barp->setUnitLabel(" kb");
	stat_barp->mPrecision = 0;
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 100000.f;
	stat_barp->mTickSpacing = 25000.f;
	stat_barp->mLabelSpacing = 50000.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mDisplayMean = FALSE;

	LLStatView *sim_time_viewp;
	sim_time_viewp = new LLStatView("sim perf view", "Time (ms)", "", rect);
	sim_statviewp->addChildAtEnd(sim_time_viewp);

	stat_barp = sim_time_viewp->addStat("Total Frame Time", &(gViewerStats->mSimFrameMsec));
	stat_barp->setUnitLabel("ms");
	stat_barp->mPrecision = 1;
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 40.f;
	stat_barp->mTickSpacing = 10.f;
	stat_barp->mLabelSpacing = 20.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mDisplayMean = FALSE;

	stat_barp = sim_time_viewp->addStat("Net Time", &(gViewerStats->mSimNetMsec));
	stat_barp->setUnitLabel("ms");
	stat_barp->mPrecision = 1;
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 40.f;
	stat_barp->mTickSpacing = 10.f;
	stat_barp->mLabelSpacing = 20.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mDisplayMean = FALSE;

	stat_barp = sim_time_viewp->addStat("Sim Time (Physics)", &(gViewerStats->mSimSimPhysicsMsec));
	stat_barp->setUnitLabel("ms");
	stat_barp->mPrecision = 1;
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 40.f;
	stat_barp->mTickSpacing = 10.f;
	stat_barp->mLabelSpacing = 20.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mDisplayMean = FALSE;

	stat_barp = sim_time_viewp->addStat("Sim Time (Other)", &(gViewerStats->mSimSimOtherMsec));
	stat_barp->setUnitLabel("ms");
	stat_barp->mPrecision = 1;
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 40.f;
	stat_barp->mTickSpacing = 10.f;
	stat_barp->mLabelSpacing = 20.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mDisplayMean = FALSE;

	stat_barp = sim_time_viewp->addStat("Agent Time", &(gViewerStats->mSimAgentMsec));
	stat_barp->setUnitLabel("ms");
	stat_barp->mPrecision = 1;
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 40.f;
	stat_barp->mTickSpacing = 10.f;
	stat_barp->mLabelSpacing = 20.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mDisplayMean = FALSE;

	stat_barp = sim_time_viewp->addStat("Images Time", &(gViewerStats->mSimImagesMsec));
	stat_barp->setUnitLabel("ms");
	stat_barp->mPrecision = 1;
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 40.f;
	stat_barp->mTickSpacing = 10.f;
	stat_barp->mLabelSpacing = 20.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mDisplayMean = FALSE;

	stat_barp = sim_time_viewp->addStat("Script Time", &(gViewerStats->mSimScriptMsec));
	stat_barp->setUnitLabel("ms");
	stat_barp->mPrecision = 1;
	stat_barp->mMinBar = 0.f;
	stat_barp->mMaxBar = 40.f;
	stat_barp->mTickSpacing = 10.f;
	stat_barp->mLabelSpacing = 20.f;
	stat_barp->mPerSec = FALSE;
	stat_barp->mDisplayBar = FALSE;
	stat_barp->mDisplayMean = FALSE;

	LLRect r = gDebugView->mStatViewp->getRect();

	// Reshape based on the parameters we set.
	gDebugView->mStatViewp->reshape(r.getWidth(), r.getHeight());
}

void asset_callback_nothing(LLVFS*, const LLUUID&, LLAssetType::EType, void*, S32)
{
	// nothing
}

// *HACK: Must match name in Library or agent inventory
const char* COMMON_GESTURES_FOLDER = "Common Gestures";
const char* MALE_GESTURES_FOLDER = "Male Gestures";
const char* FEMALE_GESTURES_FOLDER = "Female Gestures";
const char* MALE_OUTFIT_FOLDER = "Male Shape & Outfit";
const char* FEMALE_OUTFIT_FOLDER = "Female Shape & Outfit";
const S32 OPT_USE_INITIAL_OUTFIT = -2;
const S32 OPT_CLOSED_WINDOW = -1;
const S32 OPT_MALE = 0;
const S32 OPT_FEMALE = 1;

void callback_choose_gender(S32 option, void* userdata)
{
	S32 gender = OPT_FEMALE;
	const char* outfit = FEMALE_OUTFIT_FOLDER;
	const char* gestures = FEMALE_GESTURES_FOLDER;
	const char* common_gestures = COMMON_GESTURES_FOLDER;
	if (!gInitialOutfit.empty())
	{
		outfit = gInitialOutfit.c_str();
		if (gInitialOutfitGender == "male")
		{
			gender = OPT_MALE;
			gestures = MALE_GESTURES_FOLDER;
		}
		else
		{
			gender = OPT_FEMALE;
			gestures = FEMALE_GESTURES_FOLDER;
		}
	}
	else
	{
		switch(option)
		{
		case OPT_MALE:
			gender = OPT_MALE;
			outfit = MALE_OUTFIT_FOLDER;
			gestures = MALE_GESTURES_FOLDER;
			break;

		case OPT_FEMALE:
		case OPT_CLOSED_WINDOW:
		default:
			gender = OPT_FEMALE;
			outfit = FEMALE_OUTFIT_FOLDER;
			gestures = FEMALE_GESTURES_FOLDER;
			break;
		}
	}

	// try to find the outfit - if not there, create some default
	// wearables.
	LLInventoryModel::cat_array_t cat_array;
	LLInventoryModel::item_array_t item_array;
	LLNameCategoryCollector has_name(outfit);
	gInventory.collectDescendentsIf(LLUUID::null,
									cat_array,
									item_array,
									LLInventoryModel::EXCLUDE_TRASH,
									has_name);
	if (0 == cat_array.count())
	{
		gAgent.createStandardWearables(gender);
	}
	else
	{
		wear_outfit_by_name(outfit);
	}
	wear_outfit_by_name(gestures);
	wear_outfit_by_name(common_gestures);

	typedef std::map<LLUUID, LLMultiGesture*> item_map_t;
	item_map_t::iterator gestureIterator;

	// Must be here so they aren't invisible if they close the window.
	gAgent.setGenderChosen(TRUE);
}

// XUI:translate
void dialog_choose_gender_first_start()
{
	if (!gNoRender
		&& (!gAgent.isGenderChosen()))
	{
		if (!gInitialOutfit.empty())
		{
			gViewerWindow->alertXml("WelcomeNoClothes",
				callback_choose_gender, NULL);
		}
		else
		{	
			gViewerWindow->alertXml("WelcomeChooseSex",
				callback_choose_gender, NULL);
			
		}
	}
}

// Loads a bitmap to display during load
// location_id = 0 => last position
// location_id = 1 => home position
void init_start_screen(S32 location_id)
{
	if (gStartImageGL)
	{
		gStartImageGL = NULL;
		llinfos << "re-initializing start screen" << llendl;
	}

	llinfos << "Loading startup bitmap..." << llendl;

	LLString temp_str = gDirUtilp->getLindenUserDir() + gDirUtilp->getDirDelimiter();

	if ((S32)START_LOCATION_ID_LAST == location_id)
	{
		temp_str += SCREEN_LAST_FILENAME;
	}
	else
	{
		temp_str += SCREEN_HOME_FILENAME;
	}

	LLPointer<LLImageBMP> start_image_bmp = new LLImageBMP;
	if( !start_image_bmp->load(temp_str) )
	{
		llinfos << "Bitmap load failed" << llendl;
		return;
	}

	gStartImageGL = new LLImageGL(FALSE);
	gStartImageWidth = start_image_bmp->getWidth();
	gStartImageHeight = start_image_bmp->getHeight();
	LLPointer<LLImageRaw> raw = new LLImageRaw;
	if (!start_image_bmp->decode(raw))
	{
		llinfos << "Bitmap decode failed" << llendl;
		gStartImageGL = NULL;
		return;
	}

	raw->expandToPowerOfTwo();
	gStartImageGL->createGLTexture(0, raw);
}


// frees the bitmap
void release_start_screen()
{
	//llinfos << "Releasing bitmap..." << llendl;
	gStartImageGL = NULL;
}

void process_connect_to_userserver(LLMessageSystem* msg, void**)
{
	// Sent unreliably since if we've become disconnected, the
	// userserver will get back to us eventually. By sending reliable,
	// we also may accidently induce two separate validations under
	// conditions where the userserver is already lagged.
	msg->newMessage("ConnectAgentToUserserver");
	msg->nextBlockFast(_PREHASH_AgentData);
	msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
	msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
	msg->sendMessage(gUserServer);
}

bool LLStartUp::canGoFullscreen()
{
	return gStartupState >= STATE_WORLD_INIT;
}


